Merge "Revert "Enable InputDeviceMetricsCollector"" into udc-qpr-dev
diff --git a/Android.bp b/Android.bp
index 3992f82..7f1ef67 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,6 +36,14 @@
],
}
+cc_library_headers {
+ name: "native_headers",
+ host_supported: true,
+ export_include_dirs: [
+ "include/",
+ ],
+}
+
ndk_headers {
name: "libandroid_headers",
from: "include/android",
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 5dbf7ac..8a33756 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -3586,7 +3586,7 @@
// an app; they are irrelevant here because bugreport is triggered via command line.
// Update Last ID before calling Run().
Initialize();
- status = Run(-1 /* calling_uid */, "" /* calling_package */);
+ status = Run(0 /* calling_uid */, "" /* calling_package */);
}
return status;
}
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 1b7acab..c86993c 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -165,7 +165,8 @@
// Bind mount necessary directories.
constexpr const char* kBindMounts[] = {
- "/data", "/dev", "/proc", "/sys"
+ "/data", "/dev", "/proc", "/sys",
+ "/sys/fs/selinux" /* Required for apexd which includes libselinux */
};
for (size_t i = 0; i < arraysize(kBindMounts); ++i) {
std::string trg = StringPrintf("/postinstall%s", kBindMounts[i]);
diff --git a/data/etc/input/Android.bp b/data/etc/input/Android.bp
index 90f3c6b..b662491 100644
--- a/data/etc/input/Android.bp
+++ b/data/etc/input/Android.bp
@@ -3,12 +3,21 @@
}
filegroup {
- name: "motion_predictor_model.fb",
- srcs: ["motion_predictor_model.fb"],
+ name: "motion_predictor_model",
+ srcs: [
+ "motion_predictor_model.tflite",
+ "motion_predictor_config.xml",
+ ],
}
prebuilt_etc {
name: "motion_predictor_model_prebuilt",
filename_from_src: true,
- src: "motion_predictor_model.fb",
+ src: "motion_predictor_model.tflite",
+}
+
+prebuilt_etc {
+ name: "motion_predictor_model_config",
+ filename_from_src: true,
+ src: "motion_predictor_config.xml",
}
diff --git a/data/etc/input/motion_predictor_config.xml b/data/etc/input/motion_predictor_config.xml
new file mode 100644
index 0000000..03dfd63
--- /dev/null
+++ b/data/etc/input/motion_predictor_config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<motion-predictor>
+ <!-- The time interval (ns) between the model's predictions. -->
+ <prediction-interval>4166666</prediction-interval> <!-- 4.167 ms = ~240 Hz -->
+</motion-predictor>
+
diff --git a/data/etc/input/motion_predictor_model.fb b/data/etc/input/motion_predictor_model.tflite
similarity index 100%
rename from data/etc/input/motion_predictor_model.fb
rename to data/etc/input/motion_predictor_model.tflite
Binary files differ
diff --git a/services/inputflinger/tests/EventBuilders.h b/include/input/EventBuilders.h
similarity index 98%
rename from services/inputflinger/tests/EventBuilders.h
rename to include/input/EventBuilders.h
index 606a57d..09438e9 100644
--- a/services/inputflinger/tests/EventBuilders.h
+++ b/include/input/EventBuilders.h
@@ -242,6 +242,10 @@
mRawYCursorPosition = pointerCoords[0].getY();
}
+ if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+ addFlag(AMOTION_EVENT_FLAG_CANCELED);
+ }
+
return {InputEvent::nextId(),
mEventTime,
/*readTime=*/mEventTime,
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 9dedd2b..909bf08 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -40,7 +40,16 @@
// then you must add it to InputEventLabels.cpp.
class InputEventLookup {
+ /**
+ * This class is not purely static, but uses a singleton pattern in order to delay the
+ * initialization of the maps that it contains. If it were purely static, the maps could be
+ * created early, and would cause sanitizers to report memory leaks.
+ */
public:
+ InputEventLookup(InputEventLookup& other) = delete;
+
+ void operator=(const InputEventLookup&) = delete;
+
static std::optional<int> lookupValueByLabel(const std::unordered_map<std::string, int>& map,
const char* literal);
@@ -61,17 +70,24 @@
static EvdevEventLabel getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value);
private:
- static const std::unordered_map<std::string, int> KEYCODES;
+ InputEventLookup();
- static const std::vector<InputEventLabel> KEY_NAMES;
+ static const InputEventLookup& get() {
+ static InputEventLookup sLookup;
+ return sLookup;
+ }
- static const std::unordered_map<std::string, int> AXES;
+ const std::unordered_map<std::string, int> KEYCODES;
- static const std::vector<InputEventLabel> AXES_NAMES;
+ const std::vector<InputEventLabel> KEY_NAMES;
- static const std::unordered_map<std::string, int> LEDS;
+ const std::unordered_map<std::string, int> AXES;
- static const std::unordered_map<std::string, int> FLAGS;
+ const std::vector<InputEventLabel> AXES_NAMES;
+
+ const std::unordered_map<std::string, int> LEDS;
+
+ const std::unordered_map<std::string, int> FLAGS;
};
} // namespace android
diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h
index d4589f5..3715408 100644
--- a/include/input/InputVerifier.h
+++ b/include/input/InputVerifier.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -16,13 +16,25 @@
#pragma once
+#include <android-base/result.h>
#include <input/Input.h>
-#include <map>
+#include "rust/cxx.h"
namespace android {
+namespace input {
+namespace verifier {
+struct InputVerifier;
+}
+} // namespace input
+
/*
* Crash if the provided touch stream is inconsistent.
+ * This class is a pass-through to the rust implementation of InputVerifier.
+ * The rust class could also be used directly, but it would be less convenient.
+ * We can't directly invoke the rust methods on a rust object. So, there's no way to do:
+ * mVerifier.process_movement(...).
+ * This C++ class makes it a bit easier to use.
*
* TODO(b/211379801): Add support for hover events:
* - No hover move without enter
@@ -34,16 +46,13 @@
public:
InputVerifier(const std::string& name);
- void processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords, int32_t flags);
+ android::base::Result<void> processMovement(int32_t deviceId, int32_t action,
+ uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, int32_t flags);
private:
- const std::string mName;
- std::map<int32_t /*deviceId*/, std::bitset<MAX_POINTER_ID + 1>> mTouchingPointerIdsByDevice;
- void ensureTouchingPointersMatch(int32_t deviceId, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const char* action) const;
+ rust::Box<android::input::verifier::InputVerifier> mVerifier;
};
} // namespace android
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 0ca6fa3..0e3fbb1 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -27,6 +27,9 @@
template <size_t N>
std::string bitsetToString(const std::bitset<N>& bitset) {
+ if (bitset.none()) {
+ return "<none>";
+ }
return bitset.to_string();
}
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index a340bd0..fbd6026 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -25,6 +25,7 @@
#include <android-base/mapped_file.h>
#include <input/RingBuffer.h>
+#include <utils/Timers.h>
#include <tensorflow/lite/core/api/error_reporter.h>
#include <tensorflow/lite/interpreter.h>
@@ -109,6 +110,9 @@
// 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; }
+
// Executes the model.
// Returns true if the model successfully executed and the output tensors can be read.
bool invoke();
@@ -127,7 +131,8 @@
std::span<const float> outputPressure() const;
private:
- explicit TfLiteMotionPredictorModel(std::unique_ptr<android::base::MappedFile> model);
+ explicit TfLiteMotionPredictorModel(std::unique_ptr<android::base::MappedFile> model,
+ nsecs_t predictionInterval);
void allocateTensors();
void attachInputTensors();
@@ -148,6 +153,8 @@
std::unique_ptr<tflite::FlatBufferModel> mModel;
std::unique_ptr<tflite::Interpreter> mInterpreter;
tflite::SignatureRunner* mRunner = nullptr;
+
+ const nsecs_t mPredictionInterval = 0;
};
} // namespace android
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 5fbae3c..582437f 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -142,8 +142,8 @@
return appDebuggable || platformDebuggable;
}
-void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path,
- const std::string sphalLibraries) {
+void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string& path,
+ const std::string& sphalLibraries) {
if (!mDriverPath.empty() || !mSphalLibraries.empty()) {
ALOGV("ignoring attempt to change driver path from '%s' to '%s' or change sphal libraries "
"from '%s' to '%s'",
@@ -392,55 +392,24 @@
return true;
}
-void* GraphicsEnv::loadLibrary(std::string name) {
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE,
- .library_namespace = getAngleNamespace(),
- };
-
- std::string libName = std::string("lib") + name + "_angle.so";
-
- void* so = android_dlopen_ext(libName.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
-
- if (so) {
- ALOGD("dlopen_ext from APK (%s) success at %p", libName.c_str(), so);
- return so;
- } else {
- ALOGE("dlopen_ext(\"%s\") failed: %s", libName.c_str(), dlerror());
- }
-
- return nullptr;
-}
-
-bool GraphicsEnv::shouldUseAngle(std::string appName) {
- if (appName != mAngleAppName) {
- // Make sure we are checking the app we were init'ed for
- ALOGE("App name does not match: expected '%s', got '%s'", mAngleAppName.c_str(),
- appName.c_str());
- return false;
- }
-
- return shouldUseAngle();
-}
-
bool GraphicsEnv::shouldUseAngle() {
// Make sure we are init'ed
- if (mAngleAppName.empty()) {
- ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE.");
+ if (mPackageName.empty()) {
+ ALOGV("Package name is empty. setAngleInfo() has not been called to enable ANGLE.");
return false;
}
- return (mUseAngle == YES) ? true : false;
+ return (mShouldUseAngle == YES) ? true : false;
}
-void GraphicsEnv::updateUseAngle() {
+void GraphicsEnv::updateShouldUseAngle() {
const char* ANGLE_PREFER_ANGLE = "angle";
const char* ANGLE_PREFER_NATIVE = "native";
- mUseAngle = NO;
+ mShouldUseAngle = NO;
if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
ALOGV("User set \"Developer Options\" to force the use of ANGLE");
- mUseAngle = YES;
+ mShouldUseAngle = YES;
} else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
ALOGV("User set \"Developer Options\" to force the use of Native");
} else {
@@ -448,13 +417,13 @@
}
}
-void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
- const std::string developerOptIn,
+void GraphicsEnv::setAngleInfo(const std::string& path, const std::string& packageName,
+ const std::string& developerOptIn,
const std::vector<std::string> eglFeatures) {
- if (mUseAngle != UNKNOWN) {
+ if (mShouldUseAngle != UNKNOWN) {
// We've already figured out an answer for this app, so just return.
- ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
- (mUseAngle == YES) ? "true" : "false");
+ ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", packageName.c_str(),
+ (mShouldUseAngle == YES) ? "true" : "false");
return;
}
@@ -462,16 +431,17 @@
ALOGV("setting ANGLE path to '%s'", path.c_str());
mAnglePath = path;
- ALOGV("setting ANGLE app name to '%s'", appName.c_str());
- mAngleAppName = appName;
+ ALOGV("setting app package name to '%s'", packageName.c_str());
+ mPackageName = packageName;
ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str());
mAngleDeveloperOptIn = developerOptIn;
// Update the current status of whether we should use ANGLE or not
- updateUseAngle();
+ updateShouldUseAngle();
}
-void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) {
+void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace,
+ const std::string& layerPaths) {
if (mLayerPaths.empty()) {
mLayerPaths = layerPaths;
mAppNamespace = appNamespace;
@@ -485,8 +455,8 @@
return mAppNamespace;
}
-std::string& GraphicsEnv::getAngleAppName() {
- return mAngleAppName;
+std::string& GraphicsEnv::getPackageName() {
+ return mPackageName;
}
const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() {
@@ -505,11 +475,11 @@
return mDebugLayersGLES;
}
-void GraphicsEnv::setDebugLayers(const std::string layers) {
+void GraphicsEnv::setDebugLayers(const std::string& layers) {
mDebugLayers = layers;
}
-void GraphicsEnv::setDebugLayersGLES(const std::string layers) {
+void GraphicsEnv::setDebugLayersGLES(const std::string& layers) {
mDebugLayersGLES = layers;
}
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index f9b234a..a1b5e50 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -55,7 +55,7 @@
// Also set additional required sphal libraries to the linker for loading
// graphics drivers. The string is a list of libraries separated by ':',
// which is required by android_link_namespaces.
- void setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries);
+ void setDriverPathAndSphalLibraries(const std::string& path, const std::string& sphalLibraries);
// Get the updatable driver namespace.
android_namespace_t* getDriverNamespace();
std::string getDriverPath() const;
@@ -96,8 +96,6 @@
/*
* Apis for ANGLE
*/
- // Check if the requested app should use ANGLE.
- bool shouldUseAngle(std::string appName);
// Check if this app process should use ANGLE.
bool shouldUseAngle();
// Set a search path for loading ANGLE libraries. The path is a list of
@@ -105,42 +103,39 @@
// (libraries must be stored uncompressed and page aligned); such elements
// in the search path must have a '!' after the zip filename, e.g.
// /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
- void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
- const std::vector<std::string> eglFeatures);
+ void setAngleInfo(const std::string& path, const std::string& packageName,
+ const std::string& devOptIn, const std::vector<std::string> eglFeatures);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
- // Get the app name for ANGLE debug message.
- std::string& getAngleAppName();
-
+ // Get the app package name.
+ std::string& getPackageName();
const std::vector<std::string>& getAngleEglFeatures();
+ // Set the persist.graphics.egl system property value.
+ void nativeToggleAngleAsSystemDriver(bool enabled);
/*
* Apis for debug layer
*/
// Set additional layer search paths.
- void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths);
+ void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string& layerPaths);
// Get the app namespace for loading layers.
NativeLoaderNamespace* getAppNamespace();
// Get additional layer search paths.
const std::string& getLayerPaths();
// Set the Vulkan debug layers.
- void setDebugLayers(const std::string layers);
+ void setDebugLayers(const std::string& layers);
// Set the GL debug layers.
- void setDebugLayersGLES(const std::string layers);
+ void setDebugLayersGLES(const std::string& layers);
// Get the debug layers to load.
const std::string& getDebugLayers();
// Get the debug layers to load.
const std::string& getDebugLayersGLES();
- // Set the persist.graphics.egl system property value.
- void nativeToggleAngleAsSystemDriver(bool enabled);
private:
enum UseAngle { UNKNOWN, YES, NO };
- // Load requested ANGLE library.
- void* loadLibrary(std::string name);
// Update whether ANGLE should be used.
- void updateUseAngle();
+ void updateShouldUseAngle();
// Link updatable driver namespace with llndk and vndk-sp libs.
bool linkDriverNamespaceLocked(android_namespace_t* vndkNamespace);
// Check whether this process is ready to send stats.
@@ -149,39 +144,56 @@
void sendGpuStatsLocked(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime);
GraphicsEnv() = default;
+
+ // This mutex protects the namespace creation.
+ std::mutex mNamespaceMutex;
+
+ /**
+ * Updatable driver variables.
+ */
// Path to updatable driver libs.
std::string mDriverPath;
// Path to additional sphal libs linked to updatable driver namespace.
std::string mSphalLibraries;
+ // Updatable driver namespace.
+ android_namespace_t* mDriverNamespace = nullptr;
+
+ /**
+ * ANGLE variables.
+ */
+ // Path to ANGLE libs.
+ std::string mAnglePath;
+ // App's package name.
+ std::string mPackageName;
+ // ANGLE developer opt in status.
+ std::string mAngleDeveloperOptIn;
+ // ANGLE EGL features;
+ std::vector<std::string> mAngleEglFeatures;
+ // Use ANGLE flag.
+ UseAngle mShouldUseAngle = UNKNOWN;
+ // ANGLE namespace.
+ android_namespace_t* mAngleNamespace = nullptr;
+
+ /**
+ * GPU metrics.
+ */
// This mutex protects mGpuStats and get gpuservice call.
std::mutex mStatsLock;
// Cache the activity launch info
bool mActivityLaunched = false;
// Information bookkept for GpuStats.
GpuStatsInfo mGpuStats;
- // Path to ANGLE libs.
- std::string mAnglePath;
- // This App's name.
- std::string mAngleAppName;
- // ANGLE developer opt in status.
- std::string mAngleDeveloperOptIn;
- // ANGLE EGL features;
- std::vector<std::string> mAngleEglFeatures;
- // Use ANGLE flag.
- UseAngle mUseAngle = UNKNOWN;
+
+ /**
+ * Debug layers.
+ */
// Vulkan debug layers libs.
std::string mDebugLayers;
// GL debug layers libs.
std::string mDebugLayersGLES;
// Additional debug layers search path.
std::string mLayerPaths;
- // This mutex protects the namespace creation.
- std::mutex mNamespaceMutex;
- // Updatable driver namespace.
- android_namespace_t* mDriverNamespace = nullptr;
- // ANGLE namespace.
- android_namespace_t* mAngleNamespace = nullptr;
- // This App's namespace.
+ // This App's namespace to open native libraries.
NativeLoaderNamespace* mAppNamespace = nullptr;
};
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index ed69100..53a2f64 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1792,19 +1792,20 @@
int Surface::dispatchSetFrameTimelineInfo(va_list args) {
ATRACE_CALL();
- auto frameNumber = static_cast<uint64_t>(va_arg(args, uint64_t));
- auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
- auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
- auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
- auto useForRefreshRateSelection = static_cast<bool>(va_arg(args, int32_t));
-
ALOGV("Surface::%s", __func__);
+
+ const auto nativeWindowFtlInfo = static_cast<ANativeWindowFrameTimelineInfo>(
+ va_arg(args, ANativeWindowFrameTimelineInfo));
+
FrameTimelineInfo ftlInfo;
- ftlInfo.vsyncId = frameTimelineVsyncId;
- ftlInfo.inputEventId = inputEventId;
- ftlInfo.startTimeNanos = startTimeNanos;
- ftlInfo.useForRefreshRateSelection = useForRefreshRateSelection;
- return setFrameTimelineInfo(frameNumber, ftlInfo);
+ ftlInfo.vsyncId = nativeWindowFtlInfo.frameTimelineVsyncId;
+ ftlInfo.inputEventId = nativeWindowFtlInfo.inputEventId;
+ ftlInfo.startTimeNanos = nativeWindowFtlInfo.startTimeNanos;
+ ftlInfo.useForRefreshRateSelection = nativeWindowFtlInfo.useForRefreshRateSelection;
+ ftlInfo.skippedFrameVsyncId = nativeWindowFtlInfo.skippedFrameVsyncId;
+ ftlInfo.skippedFrameStartTimeNanos = nativeWindowFtlInfo.skippedFrameStartTimeNanos;
+
+ return setFrameTimelineInfo(nativeWindowFtlInfo.frameNumber, ftlInfo);
}
bool Surface::transformToDisplayInverse() const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0fda358..5bc05ef 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1027,7 +1027,7 @@
mEarlyWakeupEnd = false;
mDesiredPresentTime = 0;
mIsAutoTimestamp = true;
- clearFrameTimelineInfo(mFrameTimelineInfo);
+ mFrameTimelineInfo = {};
mApplyToken = nullptr;
mMergedTransactionIds.clear();
}
@@ -2279,27 +2279,13 @@
if (t.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
if (other.vsyncId > t.vsyncId) {
- t.vsyncId = other.vsyncId;
- t.inputEventId = other.inputEventId;
- t.startTimeNanos = other.startTimeNanos;
- t.useForRefreshRateSelection = other.useForRefreshRateSelection;
+ t = other;
}
} else if (t.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
- t.vsyncId = other.vsyncId;
- t.inputEventId = other.inputEventId;
- t.startTimeNanos = other.startTimeNanos;
- t.useForRefreshRateSelection = other.useForRefreshRateSelection;
+ t = other;
}
}
-// copied from FrameTimelineInfo::clear()
-void SurfaceComposerClient::Transaction::clearFrameTimelineInfo(FrameTimelineInfo& t) {
- t.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
- t.inputEventId = os::IInputConstants::INVALID_INPUT_EVENT_ID;
- t.startTimeNanos = 0;
- t.useForRefreshRateSelection = false;
-}
-
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::setTrustedPresentationCallback(
const sp<SurfaceControl>& sc, TrustedPresentationCallback cb,
diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING
index 9415035..a4d9e77 100644
--- a/libs/gui/TEST_MAPPING
+++ b/libs/gui/TEST_MAPPING
@@ -4,10 +4,58 @@
"path": "frameworks/native/libs/nativewindow"
}
],
- "postsubmit": [
+ "presubmit": [
{
- // TODO(257123981): move this to presubmit after dealing with existing breakages.
- "name": "libgui_test"
+ "name": "libgui_test",
+ "options": [
+ // TODO(b/277604286): Failing on Cuttlefish.
+ {
+ "exclude-filter": "MultiTextureConsumerTest#EGLImageTargetWorks"
+ },
+
+ // TODO(b/285011590): Failing on Cuttlefish.
+ {
+ "exclude-filter": "SurfaceTest#GetHdrSupport"
+ },
+ {
+ "exclude-filter": "SurfaceTest#GetWideColorSupport"
+ },
+
+ // TODO(b/285006554): Failing on Cuttlefish.
+ {
+ "exclude-filter": "SurfaceTextureGLTest#InvalidWidthOrHeightFails"
+ },
+
+ // TODO(b/277347351): Known test data issues, failing across devices.
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferNpot"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferPow2"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferWithCrop"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BuffersRepeatedly"
+ },
+
+ // TODO(b/285041169): Hanging on Cuttlefish.
+ {
+ "exclude-filter": "SurfaceTextureGLThreadToGLTest#UpdateTexImageBeforeFrameFinishedCompletes"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLThreadToGLTest#RepeatedUpdateTexImageBeforeFrameFinishedCompletes"
+ },
+ {
+ "exclude-filter": "SurfaceTextureGLThreadToGLTest#RepeatedUpdateTexImageAfterFrameFinishedCompletes"
+ },
+
+ // TODO(b/285041070): Failing on Cuttlefish.
+ {
+ "exclude-filter": "SurfaceTextureGLToGLTest#EglDestroySurfaceUnrefsBuffers"
+ }
+ ]
}
]
}
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 6df9ff1..52af9d5 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -90,8 +90,10 @@
}
parcel->writeInt32(1);
- // Ensure that the size of the flags that we use is 32 bits for writing into the parcel.
+ // Ensure that the size of custom types are what we expect for writing into the parcel.
static_assert(sizeof(inputConfig) == 4u);
+ static_assert(sizeof(ownerPid.val()) == 4u);
+ static_assert(sizeof(ownerUid.val()) == 4u);
// clang-format off
status_t status = parcel->writeStrongBinder(token) ?:
@@ -115,8 +117,8 @@
parcel->writeFloat(transform.dsdy()) ?:
parcel->writeFloat(transform.ty()) ?:
parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
- parcel->writeInt32(ownerPid) ?:
- parcel->writeInt32(ownerUid) ?:
+ parcel->writeInt32(ownerPid.val()) ?:
+ parcel->writeInt32(ownerUid.val()) ?:
parcel->writeUtf8AsUtf16(packageName) ?:
parcel->writeInt32(inputConfig.get()) ?:
parcel->writeInt32(displayId) ?:
@@ -147,7 +149,7 @@
}
float dsdx, dtdx, tx, dtdy, dsdy, ty;
- int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt;
+ int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt, ownerPidInt, ownerUidInt;
sp<IBinder> touchableRegionCropHandleSp;
// clang-format off
@@ -167,8 +169,8 @@
parcel->readFloat(&dsdy) ?:
parcel->readFloat(&ty) ?:
parcel->readInt32(&touchOcclusionModeInt) ?:
- parcel->readInt32(&ownerPid) ?:
- parcel->readInt32(&ownerUid) ?:
+ parcel->readInt32(&ownerPidInt) ?:
+ parcel->readInt32(&ownerUidInt) ?:
parcel->readUtf8FromUtf16(&packageName) ?:
parcel->readInt32(&inputConfigInt) ?:
parcel->readInt32(&displayId) ?:
@@ -190,6 +192,8 @@
transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
inputConfig = ftl::Flags<InputConfig>(inputConfigInt);
+ ownerPid = Pid{ownerPidInt};
+ ownerUid = Uid{static_cast<uid_t>(ownerUidInt)};
touchableRegionCropHandle = touchableRegionCropHandleSp;
return OK;
diff --git a/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl b/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
index 6a86c6a..4b647a4 100644
--- a/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
+++ b/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
@@ -37,4 +37,10 @@
// Whether this vsyncId should be used to heuristically select the display refresh rate
// TODO(b/281695725): Clean this up once TextureView use setFrameRate API
boolean useForRefreshRateSelection = false;
+
+ // The VsyncId of a frame that was not drawn and squashed into this frame.
+ long skippedFrameVsyncId = INVALID_VSYNC_ID;
+
+ // The start time of a frame that was not drawn and squashed into this frame.
+ long skippedFrameStartTimeNanos = 0;
}
diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
index 57720dd..95b7f39 100644
--- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
@@ -186,8 +186,8 @@
windowInfo->touchableRegion = Region(getRect(&mFdp));
windowInfo->replaceTouchableRegionWithCrop = mFdp.ConsumeBool();
windowInfo->touchOcclusionMode = mFdp.PickValueInArray(kMode);
- windowInfo->ownerPid = mFdp.ConsumeIntegral<int32_t>();
- windowInfo->ownerUid = mFdp.ConsumeIntegral<int32_t>();
+ windowInfo->ownerPid = gui::Pid{mFdp.ConsumeIntegral<pid_t>()};
+ windowInfo->ownerUid = gui::Uid{mFdp.ConsumeIntegral<uid_t>()};
windowInfo->packageName = mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes);
windowInfo->inputConfig = mFdp.PickValueInArray(kFeatures);
}
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index a6f503e..62e5f89 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -270,9 +270,9 @@
layer_state_t::eFrameRateChanged | layer_state_t::eFixedTransformHintChanged;
// Changes affecting data sent to input.
- static constexpr uint64_t INPUT_CHANGES = layer_state_t::GEOMETRY_CHANGES |
- layer_state_t::HIERARCHY_CHANGES | layer_state_t::eInputInfoChanged |
- layer_state_t::eDropInputModeChanged | layer_state_t::eTrustedOverlayChanged;
+ static constexpr uint64_t INPUT_CHANGES = layer_state_t::eInputInfoChanged |
+ layer_state_t::eDropInputModeChanged | layer_state_t::eTrustedOverlayChanged |
+ layer_state_t::eLayerStackChanged;
// Changes that affect the visible region on a display.
static constexpr uint64_t VISIBLE_REGION_CHANGES =
diff --git a/libs/gui/include/gui/PidUid.h b/libs/gui/include/gui/PidUid.h
new file mode 100644
index 0000000..7930942
--- /dev/null
+++ b/libs/gui/include/gui/PidUid.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/mixins.h>
+#include <sys/types.h>
+#include <string>
+
+namespace android::gui {
+
+// Type-safe wrapper for a PID.
+struct Pid : ftl::Constructible<Pid, pid_t>, ftl::Equatable<Pid>, ftl::Orderable<Pid> {
+ using Constructible::Constructible;
+
+ const static Pid INVALID;
+
+ constexpr auto val() const { return ftl::to_underlying(*this); }
+
+ constexpr bool isValid() const { return val() >= 0; }
+
+ std::string toString() const { return std::to_string(val()); }
+};
+
+const inline Pid Pid::INVALID{-1};
+
+// Type-safe wrapper for a UID.
+// We treat the unsigned equivalent of -1 as a singular invalid value.
+struct Uid : ftl::Constructible<Uid, uid_t>, ftl::Equatable<Uid>, ftl::Orderable<Uid> {
+ using Constructible::Constructible;
+
+ const static Uid INVALID;
+
+ constexpr auto val() const { return ftl::to_underlying(*this); }
+
+ constexpr bool isValid() const { return val() != static_cast<uid_t>(-1); }
+
+ std::string toString() const { return std::to_string(val()); }
+};
+
+const inline Uid Uid::INVALID{static_cast<uid_t>(-1)};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index fb57f63..3cf57b1 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -410,7 +410,6 @@
static sp<IBinder> sApplyToken;
void releaseBufferIfOverwriting(const layer_state_t& state);
static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
- static void clearFrameTimelineInfo(FrameTimelineInfo& t);
protected:
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 70b2ee8..7ff7387 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -21,6 +21,8 @@
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <ftl/flags.h>
+#include <ftl/mixins.h>
+#include <gui/PidUid.h>
#include <gui/constants.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -223,8 +225,8 @@
Region touchableRegion;
TouchOcclusionMode touchOcclusionMode = TouchOcclusionMode::BLOCK_UNTRUSTED;
- int32_t ownerPid = -1;
- int32_t ownerUid = -1;
+ Pid ownerPid = Pid::INVALID;
+ Uid ownerUid = Uid::INVALID;
std::string packageName;
ftl::Flags<InputConfig> inputConfig;
int32_t displayId = ADISPLAY_ID_NONE;
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index cd35d2f..462ce6e 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -21,6 +21,7 @@
],
srcs: [
+ "LibGuiMain.cpp", // Custom gtest entrypoint
"BLASTBufferQueue_test.cpp",
"BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
diff --git a/libs/gui/tests/AndroidTest.xml b/libs/gui/tests/AndroidTest.xml
index 5e09fff..31b10d7 100644
--- a/libs/gui/tests/AndroidTest.xml
+++ b/libs/gui/tests/AndroidTest.xml
@@ -23,6 +23,7 @@
<option name="screen-always-on" value="on" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
+ <option name="not-shardable" value="true" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="libgui_test" />
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index a3ad680..cd90168 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -176,18 +176,6 @@
class BLASTBufferQueueTest : public ::testing::Test {
public:
protected:
- BLASTBufferQueueTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
- }
-
- ~BLASTBufferQueueTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(), testInfo->name());
- }
-
void SetUp() {
mComposer = ComposerService::getComposerService();
mClient = new SurfaceComposerClient();
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 2f1fd3e..d585881 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "BufferQueue_test"
//#define LOG_NDEBUG 0
+#include "Constants.h"
#include "MockConsumer.h"
#include <gui/BufferItem.h>
@@ -46,20 +47,6 @@
public:
protected:
- BufferQueueTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
- }
-
- ~BufferQueueTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
- }
-
void GetMinUndequeuedBufferCount(int* bufferCount) {
ASSERT_TRUE(bufferCount != nullptr);
ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
@@ -535,7 +522,8 @@
int slot;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
@@ -578,7 +566,8 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -592,7 +581,9 @@
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -629,7 +620,8 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -656,7 +648,9 @@
// always return the same one.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -695,7 +689,8 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Enable shared buffer mode
@@ -712,7 +707,9 @@
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -747,7 +744,8 @@
for (int i = 0; i < 5; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr);
+ auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr);
if (i < 2) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
result);
@@ -774,7 +772,9 @@
for (int i = 0; i < 2; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input(0ull, true,
HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
@@ -785,7 +785,9 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
auto startTime = systemTime();
- ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(TIMED_OUT,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_GE(systemTime() - startTime, TIMEOUT);
// We're technically attaching the same buffer multiple times (since we
@@ -806,7 +808,8 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> sourceFence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -829,7 +832,8 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
sp<GraphicBuffer> firstBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer));
@@ -841,7 +845,8 @@
// Dequeue a second buffer
slot = BufferQueue::INVALID_BUFFER_SLOT;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
sp<GraphicBuffer> secondBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer));
@@ -892,8 +897,8 @@
int slots[3] = {};
mProducer->setMaxDequeuedBufferCount(3);
for (size_t i = 0; i < 3; ++i) {
- status_t result =
- mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr);
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0,
+ TEST_PRODUCER_USAGE_BITS, nullptr, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -906,7 +911,9 @@
// The first segment is a two-buffer segment, so we only put one buffer into
// the queue at a time
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -921,16 +928,22 @@
// two-buffer segment, but then at the end, we put two buffers in the queue
// at the same time before draining it.
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
std::this_thread::sleep_for(16ms);
}
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -945,10 +958,14 @@
// The third segment is a triple-buffer segment, so the queue is switching
// between one buffer and two buffers deep.
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -1047,8 +1064,8 @@
int slots[4] = {};
mProducer->setMaxDequeuedBufferCount(4);
for (size_t i = 0; i < 4; ++i) {
- status_t result =
- mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr);
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0,
+ TEST_PRODUCER_USAGE_BITS, nullptr, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -1059,14 +1076,22 @@
// Get buffers in all states: dequeued, filled, acquired, free
// Fill 3 buffers
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
// Dequeue 1 buffer
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
// Acquire and free 1 buffer
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -1132,8 +1157,8 @@
int slots[2] = {};
ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
for (size_t i = 0; i < 2; ++i) {
- status_t result =
- mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr);
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0,
+ TEST_PRODUCER_USAGE_BITS, nullptr, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -1143,10 +1168,14 @@
// Fill 2 buffers without consumer consuming them. Verify that all
// queued buffer returns proper bufferReplaced flag
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(false, output.bufferReplaced);
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(true, output.bufferReplaced);
}
@@ -1167,7 +1196,8 @@
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
// Dequeue, request, and queue one buffer
- status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr);
+ status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS,
+ nullptr, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
@@ -1182,7 +1212,9 @@
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
// Dequeue and queue the buffer again
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
// Acquire and release the buffer again. Upon acquiring, the buffer handle
@@ -1194,7 +1226,9 @@
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
// Dequeue and queue the buffer again
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr));
+ ASSERT_EQ(OK,
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, TEST_PRODUCER_USAGE_BITS, nullptr,
+ nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
// Disconnect the producer end. This should clear all of the slots and mark
diff --git a/libs/gui/tests/Constants.h b/libs/gui/tests/Constants.h
new file mode 100644
index 0000000..85c0f9f
--- /dev/null
+++ b/libs/gui/tests/Constants.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hardware/gralloc.h>
+
+// Arbitrary non-zero usage flag.
+constexpr uint64_t TEST_PRODUCER_USAGE_BITS = GRALLOC_USAGE_SW_READ_RARELY;
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 0a14afa..d80bd9c 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -62,7 +62,7 @@
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
CpuConsumerTestParams params = GetParam();
- ALOGD("** Starting test %s (%d x %d, %d, 0x%x)",
+ ALOGD("** Starting parameterized test %s (%d x %d, %d, 0x%x)",
test_info->name(),
params.width, params.height,
params.maxLockedBuffers, params.format);
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 4ec7a06..4d5bd5b 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -821,7 +821,7 @@
// with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
// Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by
// the default obscured/untrusted touch filter introduced in S.
nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW;
@@ -842,8 +842,8 @@
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = 22222;
- parentSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ parentSurface->mInputInfo.ownerUid = gui::Uid{22222};
nonTouchableSurface->showAt(0, 0);
parentSurface->showAt(100, 100);
@@ -866,8 +866,8 @@
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = 22222;
- parentSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ parentSurface->mInputInfo.ownerUid = gui::Uid{22222};
nonTouchableSurface->showAt(0, 0);
parentSurface->showAt(50, 50);
@@ -886,7 +886,7 @@
std::unique_ptr<InputSurface> bufferSurface =
InputSurface::makeBufferInputSurface(mComposerClient, 0, 0);
bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- bufferSurface->mInputInfo.ownerUid = 22222;
+ bufferSurface->mInputInfo.ownerUid = gui::Uid{22222};
surface->showAt(10, 10);
bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
@@ -901,7 +901,7 @@
std::unique_ptr<BlastInputSurface> bufferSurface =
BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- bufferSurface->mInputInfo.ownerUid = 22222;
+ bufferSurface->mInputInfo.ownerUid = gui::Uid{22222};
surface->showAt(10, 10);
bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
@@ -948,13 +948,13 @@
TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.ownerUid = 11111;
+ surface->mInputInfo.ownerUid = gui::Uid{11111};
surface->doTransaction(
[&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- obscuringSurface->mInputInfo.ownerUid = 22222;
+ obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
obscuringSurface->showAt(100, 100);
injectTap(101, 101);
EXPECT_EQ(surface->consumeEvent(100), nullptr);
@@ -967,13 +967,13 @@
TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.ownerUid = 11111;
+ surface->mInputInfo.ownerUid = gui::Uid{11111};
surface->doTransaction(
[&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- obscuringSurface->mInputInfo.ownerUid = 22222;
+ obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
obscuringSurface->showAt(190, 190);
injectTap(101, 101);
diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp
index 3ae4b6d..afeea42 100644
--- a/libs/gui/tests/GLTest.cpp
+++ b/libs/gui/tests/GLTest.cpp
@@ -29,10 +29,6 @@
}
void GLTest::SetUp() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
-
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
@@ -132,10 +128,6 @@
eglTerminate(mEglDisplay);
}
ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(), testInfo->name());
}
EGLint const* GLTest::getConfigAttribs() {
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index e6cb89c..b1f5d08 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "IGraphicBufferProducer_test"
//#define LOG_NDEBUG 0
+#include "Constants.h"
#include "MockConsumer.h"
#include <gtest/gtest.h>
@@ -40,7 +41,6 @@
#define TEST_API NATIVE_WINDOW_API_CPU
#define TEST_API_OTHER NATIVE_WINDOW_API_EGL // valid API that's not TEST_API
#define TEST_CONTROLLED_BY_APP false
-#define TEST_PRODUCER_USAGE_BITS (0)
namespace android {
@@ -82,11 +82,6 @@
IGraphicBufferProducerTest() {}
virtual void SetUp() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
-
mMC = new MockConsumer;
switch (GetParam()) {
@@ -111,13 +106,6 @@
ASSERT_OK(mConsumer->consumerConnect(mMC, /*controlledByApp*/ false));
}
- virtual void TearDown() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
- }
-
status_t TryConnectProducer() {
IGraphicBufferProducer::QueueBufferOutput output;
return mProducer->connect(TEST_TOKEN,
diff --git a/libs/gui/tests/LibGuiMain.cpp b/libs/gui/tests/LibGuiMain.cpp
new file mode 100644
index 0000000..10f7207
--- /dev/null
+++ b/libs/gui/tests/LibGuiMain.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "log/log.h"
+
+namespace {
+
+class TestCaseLogger : public ::testing::EmptyTestEventListener {
+ void OnTestStart(const ::testing::TestInfo& testInfo) override {
+ ALOGD("Begin test: %s#%s", testInfo.test_suite_name(), testInfo.name());
+ }
+
+ void OnTestEnd(const testing::TestInfo& testInfo) override {
+ ALOGD("End test: %s#%s", testInfo.test_suite_name(), testInfo.name());
+ }
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ testing::UnitTest::GetInstance()->listeners().Append(new TestCaseLogger());
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp
index 58d7cc6..376420c 100644
--- a/libs/gui/tests/Malicious.cpp
+++ b/libs/gui/tests/Malicious.cpp
@@ -151,7 +151,6 @@
sp<MaliciousBQP> malicious = getMaliciousBQP();
sp<Surface> surface = new Surface(malicious);
- ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
ANativeWindow_Buffer buffer;
ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
@@ -165,7 +164,6 @@
sp<MaliciousBQP> malicious = getMaliciousBQP();
sp<Surface> surface = new Surface(malicious);
- ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
ANativeWindow_Buffer buffer;
ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
@@ -179,7 +177,6 @@
sp<MaliciousBQP> malicious = getMaliciousBQP();
sp<Surface> surface = new Surface(malicious);
- ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
ANativeWindow_Buffer buffer;
ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
@@ -193,7 +190,6 @@
sp<MaliciousBQP> malicious = getMaliciousBQP();
sp<Surface> surface = new Surface(malicious);
- ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
ANativeWindow_Buffer buffer;
ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index 2f14924..f34b03e 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -30,23 +30,7 @@
namespace android {
-class StreamSplitterTest : public ::testing::Test {
-
-protected:
- StreamSplitterTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
- }
-
- ~StreamSplitterTest() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
- }
-};
+class StreamSplitterTest : public ::testing::Test {};
struct FakeListener : public BnConsumerListener {
virtual void onFrameAvailable(const BufferItem& /* item */) {}
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 82b6697..b28dca8 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -40,11 +40,6 @@
}
virtual void SetUp() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Begin test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
-
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
@@ -96,11 +91,6 @@
eglDestroyContext(mEglDisplay, mEglContext);
eglDestroySurface(mEglDisplay, mEglSurface);
eglTerminate(mEglDisplay);
-
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("End test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
}
virtual EGLint const* getConfigAttribs() {
diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h
index 53eb68c..9d8af5d 100644
--- a/libs/gui/tests/SurfaceTextureGL.h
+++ b/libs/gui/tests/SurfaceTextureGL.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SURFACE_TEXTURE_GL_H
#define ANDROID_SURFACE_TEXTURE_GL_H
+#include "Constants.h"
#include "GLTest.h"
#include "FrameWaiter.h"
@@ -43,6 +44,7 @@
true, false);
mSTC = new Surface(producer);
mANW = mSTC;
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), TEST_PRODUCER_USAGE_BITS));
mTextureRenderer = new TextureRenderer(TEX_ID, mST);
ASSERT_NO_FATAL_FAILURE(mTextureRenderer->SetUp());
mFW = new FrameWaiter;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 096a43c..90c0a63 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "Constants.h"
#include "MockConsumer.h"
#include <gtest/gtest.h>
@@ -148,6 +149,7 @@
/*listener*/listener));
const int BUFFER_COUNT = 4 + extraDiscardedBuffers;
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
ANativeWindowBuffer* buffers[BUFFER_COUNT];
// Dequeue first to allocate a number of buffers
@@ -530,7 +532,8 @@
ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(),
NATIVE_WINDOW_API_CPU));
- native_window_set_buffer_count(window.get(), 4);
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), 4));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
int fence;
ANativeWindowBuffer* buffer;
@@ -560,6 +563,7 @@
/*reportBufferRemoval*/true));
const int BUFFER_COUNT = 4;
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
sp<GraphicBuffer> detachedBuffer;
sp<Fence> outFence;
@@ -1202,7 +1206,8 @@
ASSERT_EQ(NO_ERROR, native_window_api_connect(mWindow.get(),
NATIVE_WINDOW_API_CPU));
- native_window_set_buffer_count(mWindow.get(), 4);
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(mWindow.get(), 4));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mWindow.get(), TEST_PRODUCER_USAGE_BITS));
}
void disableFrameTimestamps() {
@@ -2068,8 +2073,9 @@
sp<Surface> surface = new Surface(producer);
sp<ANativeWindow> window(surface);
- native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
- native_window_set_buffers_dimensions(window.get(), 0, 0);
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(window.get(), 0, 0));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
int fence;
ANativeWindowBuffer* buffer;
@@ -2121,6 +2127,7 @@
native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270);
native_window_set_buffers_dimensions(window.get(), 0, 0);
+ native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS);
ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
EXPECT_EQ(10, buffer->width);
EXPECT_EQ(20, buffer->height);
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index 11b87ef..461fe4a 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -61,8 +61,8 @@
i.alpha = 0.7;
i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
i.touchOcclusionMode = TouchOcclusionMode::ALLOW;
- i.ownerPid = 19;
- i.ownerUid = 24;
+ i.ownerPid = gui::Pid{19};
+ i.ownerUid = gui::Uid{24};
i.packageName = "com.example.package";
i.inputConfig = WindowInfo::InputConfig::NOT_FOCUSABLE;
i.displayId = 34;
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 4be7328..8a17d8a 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -33,6 +33,138 @@
],
}
+aidl_interface {
+ name: "inputconstants",
+ host_supported: true,
+ vendor_available: true,
+ unstable: true,
+ srcs: [
+ ":inputconstants_aidl",
+ ],
+
+ backend: {
+ rust: {
+ enabled: true,
+ },
+ },
+}
+
+rust_bindgen {
+ name: "libinput_bindgen",
+ host_supported: true,
+ crate_name: "input_bindgen",
+ visibility: ["//frameworks/native/services/inputflinger"],
+ wrapper_src: "InputWrapper.hpp",
+
+ include_dirs: [
+ "frameworks/native/include",
+ ],
+
+ source_stem: "bindings",
+
+ bindgen_flags: [
+ "--verbose",
+ "--allowlist-var=AMOTION_EVENT_FLAG_CANCELED",
+ "--allowlist-var=AMOTION_EVENT_ACTION_CANCEL",
+ "--allowlist-var=AMOTION_EVENT_ACTION_UP",
+ "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_DOWN",
+ "--allowlist-var=AMOTION_EVENT_ACTION_DOWN",
+ "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT",
+ "--allowlist-var=MAX_POINTER_ID",
+ ],
+
+ static_libs: [
+ "inputconstants-cpp",
+ "libui-types",
+ ],
+ shared_libs: ["libc++"],
+ header_libs: [
+ "native_headers",
+ "jni_headers",
+ "flatbuffer_headers",
+ ],
+}
+
+// Contains methods to help access C++ code from rust
+cc_library_static {
+ name: "libinput_from_rust_to_cpp",
+ cpp_std: "c++20",
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "FromRustToCpp.cpp",
+ ],
+
+ generated_headers: [
+ "cxx-bridge-header",
+ ],
+ generated_sources: ["libinput_cxx_bridge_code"],
+
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+genrule {
+ name: "libinput_cxx_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) >> $(out)",
+ srcs: ["input_verifier.rs"],
+ out: ["inputverifier_generated.cpp"],
+}
+
+genrule {
+ name: "libinput_cxx_bridge_header",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) --header >> $(out)",
+ srcs: ["input_verifier.rs"],
+ out: ["input_verifier.rs.h"],
+}
+
+rust_defaults {
+ name: "libinput_rust_defaults",
+ srcs: ["input_verifier.rs"],
+ host_supported: true,
+ rustlibs: [
+ "libbitflags",
+ "libcxx",
+ "libinput_bindgen",
+ "liblogger",
+ "liblog_rust",
+ "inputconstants-rust",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+}
+
+rust_ffi_static {
+ name: "libinput_rust",
+ crate_name: "input",
+ defaults: ["libinput_rust_defaults"],
+}
+
+rust_test {
+ name: "libinput_rust_test",
+ defaults: ["libinput_rust_defaults"],
+ whole_static_libs: [
+ "libinput_from_rust_to_cpp",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+ test_suites: ["device_tests"],
+ sanitize: {
+ hwaddress: true,
+ },
+}
+
cc_library {
name: "libinput",
cpp_std: "c++20",
@@ -44,6 +176,7 @@
"-Wno-unused-parameter",
],
srcs: [
+ "FromRustToCpp.cpp",
"Input.cpp",
"InputDevice.cpp",
"InputEventLabels.cpp",
@@ -70,14 +203,19 @@
export_header_lib_headers: ["jni_headers"],
generated_headers: [
+ "cxx-bridge-header",
+ "libinput_cxx_bridge_header",
"toolbox_input_labels",
],
+ generated_sources: ["libinput_cxx_bridge_code"],
+
shared_libs: [
"libbase",
"libcutils",
"liblog",
"libPlatformProperties",
+ "libtinyxml2",
"libvintf",
],
@@ -92,20 +230,29 @@
},
static_libs: [
+ "inputconstants-cpp",
"libui-types",
"libtflite_static",
],
+ whole_static_libs: [
+ "libinput_rust",
+ ],
+
export_static_lib_headers: [
"libui-types",
],
+ export_generated_headers: [
+ "cxx-bridge-header",
+ "libinput_cxx_bridge_header",
+ ],
+
target: {
android: {
srcs: [
"InputTransport.cpp",
"android/os/IInputFlinger.aidl",
- ":inputconstants_aidl",
],
export_shared_lib_headers: ["libbinder"],
@@ -125,6 +272,7 @@
required: [
"motion_predictor_model_prebuilt",
+ "motion_predictor_model_config",
],
},
host: {
@@ -140,12 +288,8 @@
host_linux: {
srcs: [
"InputTransport.cpp",
- "android/os/IInputConstants.aidl",
- "android/os/IInputFlinger.aidl",
- "android/os/InputConfig.aidl",
],
static_libs: [
- "libhostgraphics",
"libgui_window_info_static",
],
shared_libs: [
diff --git a/libs/input/FromRustToCpp.cpp b/libs/input/FromRustToCpp.cpp
new file mode 100644
index 0000000..e4ce62e
--- /dev/null
+++ b/libs/input/FromRustToCpp.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <ffi/FromRustToCpp.h>
+
+namespace android {
+
+bool shouldLog(rust::Str tag) {
+ return android::base::ShouldLog(android::base::LogSeverity::DEBUG, tag.data());
+}
+
+} // namespace android
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 1c7cc12..bade686 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -434,17 +434,14 @@
// clang-format on
// --- InputEventLookup ---
-const std::unordered_map<std::string, int> InputEventLookup::KEYCODES = {KEYCODES_SEQUENCE};
-const std::vector<InputEventLabel> InputEventLookup::KEY_NAMES = {KEYCODES_SEQUENCE};
-
-const std::unordered_map<std::string, int> InputEventLookup::AXES = {AXES_SEQUENCE};
-
-const std::vector<InputEventLabel> InputEventLookup::AXES_NAMES = {AXES_SEQUENCE};
-
-const std::unordered_map<std::string, int> InputEventLookup::LEDS = {LEDS_SEQUENCE};
-
-const std::unordered_map<std::string, int> InputEventLookup::FLAGS = {FLAGS_SEQUENCE};
+InputEventLookup::InputEventLookup()
+ : KEYCODES({KEYCODES_SEQUENCE}),
+ KEY_NAMES({KEYCODES_SEQUENCE}),
+ AXES({AXES_SEQUENCE}),
+ AXES_NAMES({AXES_SEQUENCE}),
+ LEDS({LEDS_SEQUENCE}),
+ FLAGS({FLAGS_SEQUENCE}) {}
std::optional<int> InputEventLookup::lookupValueByLabel(
const std::unordered_map<std::string, int>& map, const char* literal) {
@@ -462,30 +459,36 @@
}
std::optional<int> InputEventLookup::getKeyCodeByLabel(const char* label) {
- return lookupValueByLabel(KEYCODES, label);
+ const auto& self = get();
+ return self.lookupValueByLabel(self.KEYCODES, label);
}
const char* InputEventLookup::getLabelByKeyCode(int32_t keyCode) {
- if (keyCode >= 0 && static_cast<size_t>(keyCode) < KEYCODES.size()) {
- return lookupLabelByValue(KEY_NAMES, keyCode);
+ const auto& self = get();
+ if (keyCode >= 0 && static_cast<size_t>(keyCode) < self.KEYCODES.size()) {
+ return get().lookupLabelByValue(self.KEY_NAMES, keyCode);
}
return nullptr;
}
std::optional<int> InputEventLookup::getKeyFlagByLabel(const char* label) {
- return lookupValueByLabel(FLAGS, label);
+ const auto& self = get();
+ return lookupValueByLabel(self.FLAGS, label);
}
std::optional<int> InputEventLookup::getAxisByLabel(const char* label) {
- return lookupValueByLabel(AXES, label);
+ const auto& self = get();
+ return lookupValueByLabel(self.AXES, label);
}
const char* InputEventLookup::getAxisLabel(int32_t axisId) {
- return lookupLabelByValue(AXES_NAMES, axisId);
+ const auto& self = get();
+ return lookupLabelByValue(self.AXES_NAMES, axisId);
}
std::optional<int> InputEventLookup::getLedByLabel(const char* label) {
- return lookupValueByLabel(LEDS, label);
+ const auto& self = get();
+ return lookupValueByLabel(self.LEDS, label);
}
namespace {
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index f6b4648..4d3d8bc 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -4,6 +4,7 @@
// Provides a shared memory transport for input events.
//
#define LOG_TAG "InputTransport"
+#define ATRACE_TAG ATRACE_TAG_INPUT
#include <errno.h>
#include <fcntl.h>
@@ -13,6 +14,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
@@ -80,6 +82,7 @@
} // namespace
+using android::base::Result;
using android::base::StringPrintf;
namespace android {
@@ -449,6 +452,13 @@
ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", mName.c_str(),
ftl::enum_string(msg->header.type).c_str());
+
+ if (ATRACE_ENABLED()) {
+ std::string message =
+ StringPrintf("sendMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")",
+ mName.c_str(), msg->header.seq, msg->header.type);
+ ATRACE_NAME(message.c_str());
+ }
return OK;
}
@@ -484,6 +494,13 @@
ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", mName.c_str(),
ftl::enum_string(msg->header.type).c_str());
+
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf("receiveMessage(inputChannel=%s, seq=0x%" PRIx32
+ ", type=0x%" PRIx32 ")",
+ mName.c_str(), msg->header.seq, msg->header.type);
+ ATRACE_NAME(message.c_str());
+ }
return OK;
}
@@ -606,8 +623,12 @@
ATRACE_NAME(message.c_str());
}
if (verifyEvents()) {
- mInputVerifier.processMovement(deviceId, action, pointerCount, pointerProperties,
- pointerCoords, flags);
+ Result<void> result =
+ mInputVerifier.processMovement(deviceId, action, pointerCount, pointerProperties,
+ pointerCoords, flags);
+ if (!result.ok()) {
+ LOG(FATAL) << "Bad stream: " << result.error();
+ }
}
if (debugTransportPublisher()) {
std::string transformString;
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
index eb75804..32b4ca0 100644
--- a/libs/input/InputVerifier.cpp
+++ b/libs/input/InputVerifier.cpp
@@ -18,111 +18,35 @@
#include <android-base/logging.h>
#include <input/InputVerifier.h>
+#include "input_verifier.rs.h"
+
+using android::base::Error;
+using android::base::Result;
+using android::input::RustPointerProperties;
namespace android {
-/**
- * Log all of the movements that are sent to this verifier. Helps to identify the streams that lead
- * to inconsistent events.
- * Enable this via "adb shell setprop log.tag.InputVerifierLogEvents DEBUG"
- */
-static bool logEvents() {
- return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "LogEvents", ANDROID_LOG_INFO);
-}
-
// --- InputVerifier ---
-InputVerifier::InputVerifier(const std::string& name) : mName(name){};
+InputVerifier::InputVerifier(const std::string& name)
+ : mVerifier(android::input::verifier::create(name)){};
-void InputVerifier::processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords, int32_t flags) {
- if (logEvents()) {
- LOG(ERROR) << "Processing " << MotionEvent::actionToString(action) << " for device "
- << deviceId << " (" << pointerCount << " pointer"
- << (pointerCount == 1 ? "" : "s") << ") on " << mName;
+Result<void> InputVerifier::processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, int32_t flags) {
+ std::vector<RustPointerProperties> rpp;
+ for (size_t i = 0; i < pointerCount; i++) {
+ rpp.emplace_back(RustPointerProperties{.id = pointerProperties[i].id});
}
-
- switch (MotionEvent::getActionMasked(action)) {
- case AMOTION_EVENT_ACTION_DOWN: {
- auto [it, inserted] = mTouchingPointerIdsByDevice.insert({deviceId, {}});
- if (!inserted) {
- LOG(FATAL) << "Got ACTION_DOWN, but already have touching pointers " << it->second
- << " for device " << deviceId << " on " << mName;
- }
- it->second.set(pointerProperties[0].id);
- break;
- }
- case AMOTION_EVENT_ACTION_POINTER_DOWN: {
- auto it = mTouchingPointerIdsByDevice.find(deviceId);
- if (it == mTouchingPointerIdsByDevice.end()) {
- LOG(FATAL) << "Got POINTER_DOWN, but no touching pointers for device " << deviceId
- << " on " << mName;
- }
- it->second.set(pointerProperties[MotionEvent::getActionIndex(action)].id);
- break;
- }
- case AMOTION_EVENT_ACTION_MOVE: {
- ensureTouchingPointersMatch(deviceId, pointerCount, pointerProperties, "MOVE");
- break;
- }
- case AMOTION_EVENT_ACTION_POINTER_UP: {
- auto it = mTouchingPointerIdsByDevice.find(deviceId);
- if (it == mTouchingPointerIdsByDevice.end()) {
- LOG(FATAL) << "Got POINTER_UP, but no touching pointers for device " << deviceId
- << " on " << mName;
- }
- it->second.reset(pointerProperties[MotionEvent::getActionIndex(action)].id);
- break;
- }
- case AMOTION_EVENT_ACTION_UP: {
- auto it = mTouchingPointerIdsByDevice.find(deviceId);
- if (it == mTouchingPointerIdsByDevice.end()) {
- LOG(FATAL) << "Got ACTION_UP, but no record for deviceId " << deviceId << " on "
- << mName;
- }
- const auto& [_, touchingPointerIds] = *it;
- if (touchingPointerIds.count() != 1) {
- LOG(FATAL) << "Got ACTION_UP, but we have pointers: " << touchingPointerIds
- << " for deviceId " << deviceId << " on " << mName;
- }
- const int32_t pointerId = pointerProperties[0].id;
- if (!touchingPointerIds.test(pointerId)) {
- LOG(FATAL) << "Got ACTION_UP, but pointerId " << pointerId
- << " is not touching. Touching pointers: " << touchingPointerIds
- << " for deviceId " << deviceId << " on " << mName;
- }
- mTouchingPointerIdsByDevice.erase(it);
- break;
- }
- case AMOTION_EVENT_ACTION_CANCEL: {
- if ((flags & AMOTION_EVENT_FLAG_CANCELED) != AMOTION_EVENT_FLAG_CANCELED) {
- LOG(FATAL) << "For ACTION_CANCEL, must set FLAG_CANCELED";
- }
- ensureTouchingPointersMatch(deviceId, pointerCount, pointerProperties, "CANCEL");
- mTouchingPointerIdsByDevice.erase(deviceId);
- break;
- }
+ rust::Slice<const RustPointerProperties> properties{rpp.data(), rpp.size()};
+ rust::String errorMessage =
+ android::input::verifier::process_movement(*mVerifier, deviceId, action, properties,
+ flags);
+ if (errorMessage.empty()) {
+ return {};
+ } else {
+ return Error() << errorMessage;
}
}
-void InputVerifier::ensureTouchingPointersMatch(int32_t deviceId, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const char* action) const {
- auto it = mTouchingPointerIdsByDevice.find(deviceId);
- if (it == mTouchingPointerIdsByDevice.end()) {
- LOG(FATAL) << "Got " << action << ", but no touching pointers for device " << deviceId
- << " on " << mName;
- }
- const auto& [_, touchingPointerIds] = *it;
- for (size_t i = 0; i < pointerCount; i++) {
- const int32_t pointerId = pointerProperties[i].id;
- if (!touchingPointerIds.test(pointerId)) {
- LOG(FATAL) << "Got " << action << " for pointerId " << pointerId
- << " but the touching pointers are " << touchingPointerIds << " on "
- << mName;
- }
- }
-};
-
} // namespace android
diff --git a/libs/input/InputWrapper.hpp b/libs/input/InputWrapper.hpp
new file mode 100644
index 0000000..a01080d
--- /dev/null
+++ b/libs/input/InputWrapper.hpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/input.h>
+#include "input/Input.h"
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 3037573..947a956 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -36,9 +36,6 @@
namespace android {
namespace {
-const int64_t PREDICTION_INTERVAL_NANOS =
- 12500000 / 3; // TODO(b/266747937): Get this from the model.
-
/**
* Log debug messages about predictions.
* Enable this via "adb shell setprop log.tag.MotionPredictor DEBUG"
@@ -189,7 +186,7 @@
// TODO(b/266747654): Stop predictions if predicted pressure is < some threshold.
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]);
- predictionTime += PREDICTION_INTERVAL_NANOS;
+ predictionTime += mModel->predictionInterval();
if (i == 0) {
hasPredictions = true;
prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(),
@@ -208,7 +205,6 @@
axisFrom = axisTo;
axisTo = point;
}
- // TODO(b/266747511): Interpolate to futureTime?
if (!hasPredictions) {
return nullptr;
}
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index 85fa176..9f4aaa8 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -36,6 +36,7 @@
#define ATRACE_TAG ATRACE_TAG_INPUT
#include <cutils/trace.h>
#include <log/log.h>
+#include <utils/Timers.h>
#include "tensorflow/lite/core/api/error_reporter.h"
#include "tensorflow/lite/core/api/op_resolver.h"
@@ -44,6 +45,8 @@
#include "tensorflow/lite/model.h"
#include "tensorflow/lite/mutable_op_resolver.h"
+#include "tinyxml2.h"
+
namespace android {
namespace {
@@ -72,16 +75,31 @@
std::string getModelPath() {
#if defined(__ANDROID__)
- static const char* oemModel = "/vendor/etc/motion_predictor_model.fb";
+ static const char* oemModel = "/vendor/etc/motion_predictor_model.tflite";
if (fileExists(oemModel)) {
return oemModel;
}
- return "/system/etc/motion_predictor_model.fb";
+ return "/system/etc/motion_predictor_model.tflite";
#else
- return base::GetExecutableDirectory() + "/motion_predictor_model.fb";
+ return base::GetExecutableDirectory() + "/motion_predictor_model.tflite";
#endif
}
+std::string getConfigPath() {
+ // The config file should be alongside the model file.
+ return base::Dirname(getModelPath()) + "/motion_predictor_config.xml";
+}
+
+int64_t parseXMLInt64(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);
+
+ int64_t value = 0;
+ LOG_ALWAYS_FATAL_IF(element->QueryInt64Text(&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:
@@ -246,13 +264,23 @@
PLOG(FATAL) << "Failed to mmap model";
}
+ const std::string configPath = getConfigPath();
+ tinyxml2::XMLDocument configDocument;
+ LOG_ALWAYS_FATAL_IF(configDocument.LoadFile(configPath.c_str()) != tinyxml2::XML_SUCCESS,
+ "Failed to load config file from %s", configPath.c_str());
+
+ // Parse configuration file.
+ const tinyxml2::XMLElement* configRoot = configDocument.FirstChildElement("motion-predictor");
+ LOG_ALWAYS_FATAL_IF(!configRoot);
+ const nsecs_t predictionInterval = parseXMLInt64(*configRoot, "prediction-interval");
+
return std::unique_ptr<TfLiteMotionPredictorModel>(
- new TfLiteMotionPredictorModel(std::move(modelBuffer)));
+ new TfLiteMotionPredictorModel(std::move(modelBuffer), predictionInterval));
}
TfLiteMotionPredictorModel::TfLiteMotionPredictorModel(
- std::unique_ptr<android::base::MappedFile> model)
- : mFlatBuffer(std::move(model)) {
+ std::unique_ptr<android::base::MappedFile> model, nsecs_t predictionInterval)
+ : mFlatBuffer(std::move(model)), mPredictionInterval(predictionInterval) {
CHECK(mFlatBuffer);
mErrorReporter = std::make_unique<LoggingErrorReporter>();
mModel = tflite::FlatBufferModel::VerifyAndBuildFromBuffer(mFlatBuffer->data(),
diff --git a/libs/input/ffi/FromRustToCpp.h b/libs/input/ffi/FromRustToCpp.h
new file mode 100644
index 0000000..889945c
--- /dev/null
+++ b/libs/input/ffi/FromRustToCpp.h
@@ -0,0 +1,23 @@
+/*
+ * 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 "rust/cxx.h"
+
+namespace android {
+
+bool shouldLog(rust::Str tag);
+
+} // namespace android
diff --git a/libs/input/input_verifier.rs b/libs/input/input_verifier.rs
new file mode 100644
index 0000000..2e05a63
--- /dev/null
+++ b/libs/input/input_verifier.rs
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Validate the incoming motion stream.
+//! This class is not thread-safe.
+//! State is stored in the "InputVerifier" object
+//! that can be created via the 'create' method.
+//! Usage:
+//! Box<InputVerifier> verifier = create("inputChannel name");
+//! result = process_movement(verifier, ...);
+//! if (result) {
+//! crash(result.error_message());
+//! }
+
+use std::collections::HashMap;
+use std::collections::HashSet;
+
+use bitflags::bitflags;
+use log::info;
+
+#[cxx::bridge(namespace = "android::input")]
+mod ffi {
+ #[namespace = "android"]
+ unsafe extern "C++" {
+ include!("ffi/FromRustToCpp.h");
+ fn shouldLog(tag: &str) -> bool;
+ }
+ #[namespace = "android::input::verifier"]
+ extern "Rust" {
+ type InputVerifier;
+
+ fn create(name: String) -> Box<InputVerifier>;
+ fn process_movement(
+ verifier: &mut InputVerifier,
+ device_id: i32,
+ action: u32,
+ pointer_properties: &[RustPointerProperties],
+ flags: i32,
+ ) -> String;
+ }
+
+ pub struct RustPointerProperties {
+ id: i32,
+ }
+}
+
+use crate::ffi::shouldLog;
+use crate::ffi::RustPointerProperties;
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+struct DeviceId(i32);
+
+fn process_movement(
+ verifier: &mut InputVerifier,
+ device_id: i32,
+ action: u32,
+ pointer_properties: &[RustPointerProperties],
+ flags: i32,
+) -> String {
+ let result = verifier.process_movement(
+ DeviceId(device_id),
+ action,
+ pointer_properties,
+ Flags::from_bits(flags).unwrap(),
+ );
+ match result {
+ Ok(()) => "".to_string(),
+ Err(e) => e,
+ }
+}
+
+fn create(name: String) -> Box<InputVerifier> {
+ Box::new(InputVerifier::new(&name))
+}
+
+#[repr(u32)]
+enum MotionAction {
+ Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ Up = input_bindgen::AMOTION_EVENT_ACTION_UP,
+ Move = input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ Cancel = input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
+ Outside = input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE,
+ PointerDown { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN,
+ PointerUp { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP,
+ HoverEnter = input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+ HoverMove = input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
+ HoverExit = input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
+ Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL,
+ ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+}
+
+fn get_action_index(action: u32) -> usize {
+ let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+ >> input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ index.try_into().unwrap()
+}
+
+impl From<u32> for MotionAction {
+ fn from(action: u32) -> Self {
+ let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK;
+ let action_index = get_action_index(action);
+ match action_masked {
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down,
+ input_bindgen::AMOTION_EVENT_ACTION_UP => MotionAction::Up,
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE => MotionAction::Move,
+ input_bindgen::AMOTION_EVENT_ACTION_CANCEL => MotionAction::Cancel,
+ input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE => MotionAction::Outside,
+ input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN => {
+ MotionAction::PointerDown { action_index }
+ }
+ input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP => {
+ MotionAction::PointerUp { action_index }
+ }
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER => MotionAction::HoverEnter,
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove,
+ input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit,
+ input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll,
+ input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress,
+ input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease,
+ _ => panic!("Unknown action: {}", action),
+ }
+ }
+}
+
+bitflags! {
+ struct Flags: i32 {
+ const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED;
+ }
+}
+
+fn motion_action_to_string(action: u32) -> String {
+ match action.into() {
+ MotionAction::Down => "DOWN".to_string(),
+ MotionAction::Up => "UP".to_string(),
+ MotionAction::Move => "MOVE".to_string(),
+ MotionAction::Cancel => "CANCEL".to_string(),
+ MotionAction::Outside => "OUTSIDE".to_string(),
+ MotionAction::PointerDown { action_index } => {
+ format!("POINTER_DOWN({})", action_index)
+ }
+ MotionAction::PointerUp { action_index } => {
+ format!("POINTER_UP({})", action_index)
+ }
+ MotionAction::HoverMove => "HOVER_MOVE".to_string(),
+ MotionAction::Scroll => "SCROLL".to_string(),
+ MotionAction::HoverEnter => "HOVER_ENTER".to_string(),
+ MotionAction::HoverExit => "HOVER_EXIT".to_string(),
+ MotionAction::ButtonPress => "BUTTON_PRESS".to_string(),
+ MotionAction::ButtonRelease => "BUTTON_RELEASE".to_string(),
+ }
+}
+
+/**
+ * Log all of the movements that are sent to this verifier. Helps to identify the streams that lead
+ * to inconsistent events.
+ * Enable this via "adb shell setprop log.tag.InputVerifierLogEvents DEBUG"
+ */
+fn log_events() -> bool {
+ shouldLog("InputVerifierLogEvents")
+}
+
+struct InputVerifier {
+ name: String,
+ touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
+}
+
+impl InputVerifier {
+ fn new(name: &str) -> Self {
+ logger::init(
+ logger::Config::default()
+ .with_tag_on_device("InputVerifier")
+ .with_min_level(log::Level::Trace),
+ );
+ Self { name: name.to_owned(), touching_pointer_ids_by_device: HashMap::new() }
+ }
+
+ fn process_movement(
+ &mut self,
+ device_id: DeviceId,
+ action: u32,
+ pointer_properties: &[RustPointerProperties],
+ flags: Flags,
+ ) -> Result<(), String> {
+ if log_events() {
+ info!(
+ "Processing {} for device {:?} ({} pointer{}) on {}",
+ motion_action_to_string(action),
+ device_id,
+ pointer_properties.len(),
+ if pointer_properties.len() == 1 { "" } else { "s" },
+ self.name
+ );
+ }
+
+ match action.into() {
+ MotionAction::Down => {
+ let it = self
+ .touching_pointer_ids_by_device
+ .entry(device_id)
+ .or_insert_with(HashSet::new);
+ let pointer_id = pointer_properties[0].id;
+ if it.contains(&pointer_id) {
+ return Err(format!(
+ "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
+ self.name, device_id, it
+ ));
+ }
+ it.insert(pointer_id);
+ }
+ MotionAction::PointerDown { action_index } => {
+ if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{}: Received POINTER_DOWN but no pointers are currently down \
+ for device {:?}",
+ self.name, device_id
+ ));
+ }
+ let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ let pointer_id = pointer_properties[action_index].id;
+ if it.contains(&pointer_id) {
+ return Err(format!(
+ "{}: Pointer with id={} not found in the properties",
+ self.name, pointer_id
+ ));
+ }
+ it.insert(pointer_id);
+ }
+ MotionAction::Move => {
+ if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+ return Err(format!(
+ "{}: ACTION_MOVE touching pointers don't match",
+ self.name
+ ));
+ }
+ }
+ MotionAction::PointerUp { action_index } => {
+ if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{}: Received POINTER_UP but no pointers are currently down for device \
+ {:?}",
+ self.name, device_id
+ ));
+ }
+ let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ let pointer_id = pointer_properties[action_index].id;
+ it.remove(&pointer_id);
+ }
+ MotionAction::Up => {
+ if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+ return Err(format!(
+ "{} Received ACTION_UP but no pointers are currently down for device {:?}",
+ self.name, device_id
+ ));
+ }
+ let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+ if it.len() != 1 {
+ return Err(format!(
+ "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
+ self.name, it, device_id
+ ));
+ }
+ let pointer_id = pointer_properties[0].id;
+ if !it.contains(&pointer_id) {
+ return Err(format!(
+ "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
+ {:?} for device {:?}",
+ self.name, pointer_id, it, device_id
+ ));
+ }
+ it.clear();
+ }
+ MotionAction::Cancel => {
+ if flags.contains(Flags::CANCELED) {
+ return Err(format!(
+ "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
+ self.name
+ ));
+ }
+ if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+ return Err(format!(
+ "{}: Got ACTION_CANCEL, but the pointers don't match. \
+ Existing pointers: {:?}",
+ self.name, self.touching_pointer_ids_by_device
+ ));
+ }
+ self.touching_pointer_ids_by_device.remove(&device_id);
+ }
+ _ => return Ok(()),
+ }
+ Ok(())
+ }
+
+ fn ensure_touching_pointers_match(
+ &self,
+ device_id: DeviceId,
+ pointer_properties: &[RustPointerProperties],
+ ) -> bool {
+ let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
+ return false;
+ };
+
+ for pointer_property in pointer_properties.iter() {
+ let pointer_id = pointer_property.id;
+ if !pointers.contains(&pointer_id) {
+ return false;
+ }
+ }
+ true
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::DeviceId;
+ use crate::Flags;
+ use crate::InputVerifier;
+ use crate::RustPointerProperties;
+ #[test]
+ fn single_pointer_stream() {
+ let mut verifier = InputVerifier::new("Test");
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ Flags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ Flags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_UP,
+ &pointer_properties,
+ Flags::empty(),
+ )
+ .is_ok());
+ }
+
+ #[test]
+ fn multi_device_stream() {
+ let mut verifier = InputVerifier::new("Test");
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ Flags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ Flags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(2),
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ Flags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(2),
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ Flags::empty(),
+ )
+ .is_ok());
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_UP,
+ &pointer_properties,
+ Flags::empty(),
+ )
+ .is_ok());
+ }
+
+ #[test]
+ fn test_invalid_up() {
+ let mut verifier = InputVerifier::new("Test");
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ input_bindgen::AMOTION_EVENT_ACTION_UP,
+ &pointer_properties,
+ Flags::empty(),
+ )
+ .is_err());
+ }
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 6aae25d..cadac88 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -64,12 +64,13 @@
"libcutils",
"liblog",
"libPlatformProperties",
+ "libtinyxml2",
"libutils",
"libvintf",
],
data: [
"data/*",
- ":motion_predictor_model.fb",
+ ":motion_predictor_model",
],
test_options: {
unit_test: true,
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 0fee3c1..edaa422 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1066,12 +1066,33 @@
(int)compatibility, (int)changeFrameRateStrategy);
}
+struct ANativeWindowFrameTimelineInfo {
+ // Frame Id received from ANativeWindow_getNextFrameId.
+ uint64_t frameNumber;
+
+ // VsyncId received from the Choreographer callback that started this frame.
+ int64_t frameTimelineVsyncId;
+
+ // Input Event ID received from the input event that started this frame.
+ int32_t inputEventId;
+
+ // The time which this frame rendering started (i.e. when Choreographer callback actually run)
+ int64_t startTimeNanos;
+
+ // Whether or not to use the vsyncId to determine the refresh rate. Used for TextureView only.
+ int32_t useForRefreshRateSelection;
+
+ // The VsyncId of a frame that was not drawn and squashed into this frame.
+ // Used for UI thread updates that were not picked up by RenderThread on time.
+ int64_t skippedFrameVsyncId;
+
+ // The start time of a frame that was not drawn and squashed into this frame.
+ int64_t skippedFrameStartTimeNanos;
+};
+
static inline int native_window_set_frame_timeline_info(
- struct ANativeWindow* window, uint64_t frameNumber, int64_t frameTimelineVsyncId,
- int32_t inputEventId, int64_t startTimeNanos, int32_t useForRefreshRateSelection) {
- return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameNumber,
- frameTimelineVsyncId, inputEventId, startTimeNanos,
- useForRefreshRateSelection);
+ struct ANativeWindow* window, struct ANativeWindowFrameTimelineInfo frameTimelineInfo) {
+ return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineInfo);
}
// ------------------------------------------------------------------------------------------------
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index c412c9c..23c99b0 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -86,14 +86,38 @@
void logFatalTexture(const char* msg, const GrBackendTexture& tex, ui::Dataspace dataspace,
SkColorType colorType) {
- GrGLTextureInfo textureInfo;
- bool retrievedTextureInfo = tex.getGLTextureInfo(&textureInfo);
- LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
- "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i texType: %i"
- "\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u colorType %i",
- msg, tex.isValid(), dataspace, tex.width(), tex.height(), tex.hasMipmaps(),
- tex.isProtected(), static_cast<int>(tex.textureType()), retrievedTextureInfo,
- textureInfo.fTarget, textureInfo.fFormat, colorType);
+ switch (tex.backend()) {
+ case GrBackendApi::kOpenGL: {
+ GrGLTextureInfo textureInfo;
+ bool retrievedTextureInfo = tex.getGLTextureInfo(&textureInfo);
+ LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
+ "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
+ "texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u"
+ " colorType %i",
+ msg, tex.isValid(), dataspace, tex.width(), tex.height(),
+ tex.hasMipmaps(), tex.isProtected(),
+ static_cast<int>(tex.textureType()), retrievedTextureInfo,
+ textureInfo.fTarget, textureInfo.fFormat, colorType);
+ break;
+ }
+ case GrBackendApi::kVulkan: {
+ GrVkImageInfo imageInfo;
+ bool retrievedImageInfo = tex.getVkImageInfo(&imageInfo);
+ LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
+ "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
+ "texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i "
+ "fSampleCount: %u fLevelCount: %u colorType %i",
+ msg, tex.isValid(), dataspace, tex.width(), tex.height(),
+ tex.hasMipmaps(), tex.isProtected(),
+ static_cast<int>(tex.textureType()), retrievedImageInfo,
+ imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount,
+ colorType);
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("%s Unexpected backend %u", msg, static_cast<unsigned>(tex.backend()));
+ break;
+ }
}
sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 5854135..18d9864 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -86,6 +86,7 @@
// Debugging settings
static const bool kPrintLayerSettings = false;
static const bool kFlushAfterEveryLayer = kPrintLayerSettings;
+static constexpr bool kEnableLayerBrightening = true;
} // namespace
@@ -664,6 +665,8 @@
validateOutputBufferUsage(buffer->getBuffer());
auto grContext = getActiveGrContext();
+ LOG_ALWAYS_FATAL_IF(grContext->abandoned(), "GrContext is abandoned/device lost at start of %s",
+ __func__);
// any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
DeferTextureCleanup dtc(mTextureCleanupMgr);
@@ -699,7 +702,8 @@
// ...and compute the dimming ratio if dimming is requested
const float displayDimmingRatio = display.targetLuminanceNits > 0.f &&
- maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint
+ maxLayerWhitePoint > 0.f &&
+ (kEnableLayerBrightening || display.targetLuminanceNits > maxLayerWhitePoint)
? maxLayerWhitePoint / display.targetLuminanceNits
: 1.f;
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index ac75f43..334106f 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -142,6 +142,8 @@
std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID};
};
+using FenceTimePtr = std::shared_ptr<FenceTime>;
+
// A queue of FenceTimes that are expected to signal in FIFO order.
// Only maintains a queue of weak pointers so it doesn't keep references
// to Fences on its own.
@@ -190,8 +192,15 @@
// before the new one is added.
class FenceToFenceTimeMap {
public:
- // Create a new FenceTime with that wraps the provided Fence.
- std::shared_ptr<FenceTime> createFenceTimeForTest(const sp<Fence>& fence);
+ using FencePair = std::pair<sp<Fence>, FenceTimePtr>;
+
+ FencePair makePendingFenceForTest() {
+ const auto fence = sp<Fence>::make();
+ return {fence, createFenceTimeForTest(fence)};
+ }
+
+ // Create a new FenceTime that wraps the provided Fence.
+ FenceTimePtr createFenceTimeForTest(const sp<Fence>&);
// Signals all FenceTimes created through this class that are wrappers
// around |fence|.
@@ -205,7 +214,6 @@
std::unordered_map<Fence*, std::vector<std::weak_ptr<FenceTime>>> mMap;
};
-
-}; // namespace android
+} // namespace android
#endif // ANDROID_FENCE_TIME_H
diff --git a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
index acb9b79..bbe58e0 100644
--- a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
+++ b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
@@ -249,7 +249,7 @@
jpegGainMap.data = gainMapEncoder.getCompressedImagePtr();
jpegGainMap.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
ultrahdr_metadata_struct metadata;
- metadata.version = "1.3.1";
+ metadata.version = "1.0";
if (tf == ULTRAHDR_TF_HLG) {
metadata.maxContentBoost = kHlgMaxNits / kSdrWhiteNits;
} else if (tf == ULTRAHDR_TF_PQ) {
@@ -258,6 +258,11 @@
metadata.maxContentBoost = 1.0f;
}
metadata.minContentBoost = 1.0f;
+ metadata.gamma = 1.0f;
+ metadata.offsetSdr = 0.0f;
+ metadata.offsetHdr = 0.0f;
+ metadata.hdrCapacityMin = 1.0f;
+ metadata.hdrCapacityMax = metadata.maxContentBoost;
status = jpegHdr.encodeJPEGR(&jpegImg, &jpegGainMap, &metadata, &jpegImgR);
}
}
diff --git a/libs/ultrahdr/include/ultrahdr/gainmapmath.h b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
index 13832db..edf152d 100644
--- a/libs/ultrahdr/include/ultrahdr/gainmapmath.h
+++ b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
@@ -414,6 +414,10 @@
/*
* Calculate the 8-bit unsigned integer gain value for the given SDR and HDR
* luminances in linear space, and the hdr ratio to encode against.
+ *
+ * Note: since this library always uses gamma of 1.0, offsetSdr of 0.0, and
+ * offsetHdr of 0.0, this function doesn't handle different metadata values for
+ * these fields.
*/
uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata);
uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata,
@@ -422,6 +426,10 @@
/*
* Calculates the linear luminance in nits after applying the given gain
* value, with the given hdr ratio, to the given sdr input in the range [0, 1].
+ *
+ * Note: similar to encodeGain(), this function only supports gamma 1.0,
+ * offsetSdr 0.0, offsetHdr 0.0, hdrCapacityMin 1.0, and hdrCapacityMax equal to
+ * gainMapMax, as this library encodes.
*/
Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata);
Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata, float displayBoost);
diff --git a/libs/ultrahdr/include/ultrahdr/jpegr.h b/libs/ultrahdr/include/ultrahdr/jpegr.h
index 9546ca4..a35fd30 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegr.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegr.h
@@ -222,7 +222,11 @@
* Decompress JPEGR image.
*
* This method assumes that the JPEGR image contains an ICC profile with primaries that match
- * those of a color gamut that this library is aware of; Bt.709, Display-P3, or Bt.2100.
+ * those of a color gamut that this library is aware of; Bt.709, Display-P3, or Bt.2100. It also
+ * assumes the base image uses the sRGB transfer function.
+ *
+ * This method only supports single gain map metadata values for fields that allow multi-channel
+ * metadata values.
*
* @param compressed_jpegr_image compressed JPEGR image.
* @param dest destination of the uncompressed JPEGR image.
@@ -265,6 +269,9 @@
/*
* Gets Info from JPEGR file without decoding it.
*
+ * This method only supports single gain map metadata values for fields that allow multi-channel
+ * metadata values.
+ *
* The output is filled jpegr_info structure
* @param compressed_jpegr_image compressed JPEGR image
* @param jpegr_info pointer to output JPEGR info. Members of jpegr_info
diff --git a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
index 9f59c3e..0641232 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
@@ -42,6 +42,8 @@
ERROR_JPEGR_BUFFER_TOO_SMALL = JPEGR_IO_ERROR_BASE - 4,
ERROR_JPEGR_INVALID_COLORGAMUT = JPEGR_IO_ERROR_BASE - 5,
ERROR_JPEGR_INVALID_TRANS_FUNC = JPEGR_IO_ERROR_BASE - 6,
+ ERROR_JPEGR_INVALID_METADATA = JPEGR_IO_ERROR_BASE - 7,
+ ERROR_JPEGR_UNSUPPORTED_METADATA = JPEGR_IO_ERROR_BASE - 8,
JPEGR_RUNTIME_ERROR_BASE = -20000,
ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1,
diff --git a/libs/ultrahdr/include/ultrahdr/ultrahdr.h b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
index 21751b4..17cc971 100644
--- a/libs/ultrahdr/include/ultrahdr/ultrahdr.h
+++ b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
@@ -49,14 +49,28 @@
/*
* Holds information for gain map related metadata.
+ *
+ * Not: all values stored in linear. This differs from the metadata encoding in XMP, where
+ * maxContentBoost (aka gainMapMax), minContentBoost (aka gainMapMin), hdrCapacityMin, and
+ * hdrCapacityMax are stored in log2 space.
*/
struct ultrahdr_metadata_struct {
- // Ultra HDR library version
+ // Ultra HDR format version
std::string version;
// Max Content Boost for the map
float maxContentBoost;
// Min Content Boost for the map
float minContentBoost;
+ // Gamma of the map data
+ float gamma;
+ // Offset for SDR data in map calculations
+ float offsetSdr;
+ // Offset for HDR data in map calculations
+ float offsetHdr;
+ // HDR capacity to apply the map at all
+ float hdrCapacityMin;
+ // HDR capacity to apply the map completely
+ float hdrCapacityMax;
};
typedef struct ultrahdr_metadata_struct* ultrahdr_metadata_ptr;
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
index 9af5af7..9c57f34 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -644,13 +644,18 @@
ultrahdr_metadata_struct uhdr_metadata;
if (!getMetadataFromXMP(static_cast<uint8_t*>(gain_map_decoder.getXMPPtr()),
gain_map_decoder.getXMPSize(), &uhdr_metadata)) {
- return ERROR_JPEGR_DECODE_ERROR;
+ return ERROR_JPEGR_INVALID_METADATA;
}
if (metadata != nullptr) {
metadata->version = uhdr_metadata.version;
metadata->minContentBoost = uhdr_metadata.minContentBoost;
metadata->maxContentBoost = uhdr_metadata.maxContentBoost;
+ metadata->gamma = uhdr_metadata.gamma;
+ metadata->offsetSdr = uhdr_metadata.offsetSdr;
+ metadata->offsetHdr = uhdr_metadata.offsetHdr;
+ metadata->hdrCapacityMin = uhdr_metadata.hdrCapacityMin;
+ metadata->hdrCapacityMax = uhdr_metadata.hdrCapacityMax;
}
if (output_format == ULTRAHDR_OUTPUT_SDR) {
@@ -840,6 +845,12 @@
metadata->maxContentBoost = hdr_white_nits / kSdrWhiteNits;
metadata->minContentBoost = 1.0f;
+ metadata->gamma = 1.0f;
+ metadata->offsetSdr = 0.0f;
+ metadata->offsetHdr = 0.0f;
+ metadata->hdrCapacityMin = 1.0f;
+ metadata->hdrCapacityMax = metadata->maxContentBoost;
+
float log2MinBoost = log2(metadata->minContentBoost);
float log2MaxBoost = log2(metadata->maxContentBoost);
@@ -958,6 +969,26 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
+ if (metadata->version.compare("1.0")) {
+ ALOGE("Unsupported metadata version: %s", metadata->version.c_str());
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ }
+ if (metadata->gamma != 1.0f) {
+ ALOGE("Unsupported metadata gamma: %f", metadata->gamma);
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ }
+ if (metadata->offsetSdr != 0.0f || metadata->offsetHdr != 0.0f) {
+ ALOGE("Unsupported metadata offset sdr, hdr: %f, %f", metadata->offsetSdr,
+ metadata->offsetHdr);
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ }
+ if (metadata->hdrCapacityMin != metadata->minContentBoost
+ || metadata->hdrCapacityMax != metadata->maxContentBoost) {
+ ALOGE("Unsupported metadata hdr capacity min, max: %f, %f", metadata->hdrCapacityMin,
+ metadata->hdrCapacityMax);
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ }
+
// TODO: remove once map scaling factor is computed based on actual map dims
size_t image_width = uncompressed_yuv_420_image->width;
size_t image_height = uncompressed_yuv_420_image->height;
@@ -1180,12 +1211,33 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- if (metadata->minContentBoost < 1.0f || metadata->maxContentBoost < metadata->minContentBoost) {
+ if (metadata->version.compare("1.0")) {
+ ALOGE("received bad value for version: %s", metadata->version.c_str());
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+ if (metadata->maxContentBoost < metadata->minContentBoost) {
ALOGE("received bad value for content boost min %f, max %f", metadata->minContentBoost,
metadata->maxContentBoost);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
+ if (metadata->hdrCapacityMax < metadata->hdrCapacityMin || metadata->hdrCapacityMin < 1.0f) {
+ ALOGE("received bad value for hdr capacity min %f, max %f", metadata->hdrCapacityMin,
+ metadata->hdrCapacityMax);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (metadata->offsetSdr < 0.0f || metadata->offsetHdr < 0.0f) {
+ ALOGE("received bad value for offset sdr %f, hdr %f", metadata->offsetSdr,
+ metadata->offsetHdr);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (metadata->gamma <= 0.0f) {
+ ALOGE("received bad value for gamma %f", metadata->gamma);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
const string nameSpace = "http://ns.adobe.com/xap/1.0/";
const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
diff --git a/libs/ultrahdr/jpegrutils.cpp b/libs/ultrahdr/jpegrutils.cpp
index 6430af1..c434eb6 100644
--- a/libs/ultrahdr/jpegrutils.cpp
+++ b/libs/ultrahdr/jpegrutils.cpp
@@ -113,6 +113,15 @@
XMPXmlHandler() : XmlHandler() {
state = NotStrarted;
+ versionFound = false;
+ minContentBoostFound = false;
+ maxContentBoostFound = false;
+ gammaFound = false;
+ offsetSdrFound = false;
+ offsetHdrFound = false;
+ hdrCapacityMinFound = false;
+ hdrCapacityMaxFound = false;
+ baseRenditionIsHdrFound = false;
}
enum ParseState {
@@ -147,10 +156,24 @@
string val;
if (state == Started) {
if (context.BuildTokenValue(&val)) {
- if (!val.compare(maxContentBoostAttrName)) {
+ if (!val.compare(versionAttrName)) {
+ lastAttributeName = versionAttrName;
+ } else if (!val.compare(maxContentBoostAttrName)) {
lastAttributeName = maxContentBoostAttrName;
} else if (!val.compare(minContentBoostAttrName)) {
lastAttributeName = minContentBoostAttrName;
+ } else if (!val.compare(gammaAttrName)) {
+ lastAttributeName = gammaAttrName;
+ } else if (!val.compare(offsetSdrAttrName)) {
+ lastAttributeName = offsetSdrAttrName;
+ } else if (!val.compare(offsetHdrAttrName)) {
+ lastAttributeName = offsetHdrAttrName;
+ } else if (!val.compare(hdrCapacityMinAttrName)) {
+ lastAttributeName = hdrCapacityMinAttrName;
+ } else if (!val.compare(hdrCapacityMaxAttrName)) {
+ lastAttributeName = hdrCapacityMaxAttrName;
+ } else if (!val.compare(baseRenditionIsHdrAttrName)) {
+ lastAttributeName = baseRenditionIsHdrAttrName;
} else {
lastAttributeName = "";
}
@@ -163,18 +186,52 @@
string val;
if (state == Started) {
if (context.BuildTokenValue(&val, true)) {
- if (!lastAttributeName.compare(maxContentBoostAttrName)) {
+ if (!lastAttributeName.compare(versionAttrName)) {
+ versionStr = val;
+ versionFound = true;
+ } else if (!lastAttributeName.compare(maxContentBoostAttrName)) {
maxContentBoostStr = val;
+ maxContentBoostFound = true;
} else if (!lastAttributeName.compare(minContentBoostAttrName)) {
minContentBoostStr = val;
+ minContentBoostFound = true;
+ } else if (!lastAttributeName.compare(gammaAttrName)) {
+ gammaStr = val;
+ gammaFound = true;
+ } else if (!lastAttributeName.compare(offsetSdrAttrName)) {
+ offsetSdrStr = val;
+ offsetSdrFound = true;
+ } else if (!lastAttributeName.compare(offsetHdrAttrName)) {
+ offsetHdrStr = val;
+ offsetHdrFound = true;
+ } else if (!lastAttributeName.compare(hdrCapacityMinAttrName)) {
+ hdrCapacityMinStr = val;
+ hdrCapacityMinFound = true;
+ } else if (!lastAttributeName.compare(hdrCapacityMaxAttrName)) {
+ hdrCapacityMaxStr = val;
+ hdrCapacityMaxFound = true;
+ } else if (!lastAttributeName.compare(baseRenditionIsHdrAttrName)) {
+ baseRenditionIsHdrStr = val;
+ baseRenditionIsHdrFound = true;
}
}
}
return context.GetResult();
}
- bool getMaxContentBoost(float* max_content_boost) {
+ bool getVersion(string* version, bool* present) {
if (state == Done) {
+ *version = versionStr;
+ *present = versionFound;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ bool getMaxContentBoost(float* max_content_boost, bool* present) {
+ if (state == Done) {
+ *present = maxContentBoostFound;
stringstream ss(maxContentBoostStr);
float val;
if (ss >> val) {
@@ -188,8 +245,9 @@
}
}
- bool getMinContentBoost(float* min_content_boost) {
+ bool getMinContentBoost(float* min_content_boost, bool* present) {
if (state == Done) {
+ *present = minContentBoostFound;
stringstream ss(minContentBoostStr);
float val;
if (ss >> val) {
@@ -203,12 +261,141 @@
}
}
+ bool getGamma(float* gamma, bool* present) {
+ if (state == Done) {
+ *present = gammaFound;
+ stringstream ss(gammaStr);
+ float val;
+ if (ss >> val) {
+ *gamma = val;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ bool getOffsetSdr(float* offset_sdr, bool* present) {
+ if (state == Done) {
+ *present = offsetSdrFound;
+ stringstream ss(offsetSdrStr);
+ float val;
+ if (ss >> val) {
+ *offset_sdr = val;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ bool getOffsetHdr(float* offset_hdr, bool* present) {
+ if (state == Done) {
+ *present = offsetHdrFound;
+ stringstream ss(offsetHdrStr);
+ float val;
+ if (ss >> val) {
+ *offset_hdr = val;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ bool getHdrCapacityMin(float* hdr_capacity_min, bool* present) {
+ if (state == Done) {
+ *present = hdrCapacityMinFound;
+ stringstream ss(hdrCapacityMinStr);
+ float val;
+ if (ss >> val) {
+ *hdr_capacity_min = exp2(val);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ bool getHdrCapacityMax(float* hdr_capacity_max, bool* present) {
+ if (state == Done) {
+ *present = hdrCapacityMaxFound;
+ stringstream ss(hdrCapacityMaxStr);
+ float val;
+ if (ss >> val) {
+ *hdr_capacity_max = exp2(val);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ bool getBaseRenditionIsHdr(bool* base_rendition_is_hdr, bool* present) {
+ if (state == Done) {
+ *present = baseRenditionIsHdrFound;
+ if (!baseRenditionIsHdrStr.compare("False")) {
+ *base_rendition_is_hdr = false;
+ return true;
+ } else if (!baseRenditionIsHdrStr.compare("True")) {
+ *base_rendition_is_hdr = true;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+
private:
static const string containerName;
+
+ static const string versionAttrName;
+ string versionStr;
+ bool versionFound;
static const string maxContentBoostAttrName;
string maxContentBoostStr;
+ bool maxContentBoostFound;
static const string minContentBoostAttrName;
string minContentBoostStr;
+ bool minContentBoostFound;
+ static const string gammaAttrName;
+ string gammaStr;
+ bool gammaFound;
+ static const string offsetSdrAttrName;
+ string offsetSdrStr;
+ bool offsetSdrFound;
+ static const string offsetHdrAttrName;
+ string offsetHdrStr;
+ bool offsetHdrFound;
+ static const string hdrCapacityMinAttrName;
+ string hdrCapacityMinStr;
+ bool hdrCapacityMinFound;
+ static const string hdrCapacityMaxAttrName;
+ string hdrCapacityMaxStr;
+ bool hdrCapacityMaxFound;
+ static const string baseRenditionIsHdrAttrName;
+ string baseRenditionIsHdrStr;
+ bool baseRenditionIsHdrFound;
+
string lastAttributeName;
ParseState state;
};
@@ -253,8 +440,15 @@
const string kMapBaseRenditionIsHDR = Name(kGainMapPrefix, "BaseRenditionIsHDR");
// GainMap XMP constants - names for XMP handlers
+const string XMPXmlHandler::versionAttrName = kMapVersion;
const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
+const string XMPXmlHandler::gammaAttrName = kMapGamma;
+const string XMPXmlHandler::offsetSdrAttrName = kMapOffsetSdr;
+const string XMPXmlHandler::offsetHdrAttrName = kMapOffsetHdr;
+const string XMPXmlHandler::hdrCapacityMinAttrName = kMapHDRCapacityMin;
+const string XMPXmlHandler::hdrCapacityMaxAttrName = kMapHDRCapacityMax;
+const string XMPXmlHandler::baseRenditionIsHdrAttrName = kMapBaseRenditionIsHDR;
bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, ultrahdr_metadata_struct* metadata) {
string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -291,11 +485,48 @@
return false;
}
- if (!handler.getMaxContentBoost(&metadata->maxContentBoost)) {
+ // Apply default values to any not-present fields, except for Version,
+ // maxContentBoost, and hdrCapacityMax, which are required. Return false if
+ // we encounter a present field that couldn't be parsed, since this
+ // indicates it is invalid (eg. string where there should be a float).
+ bool present = false;
+ if (!handler.getVersion(&metadata->version, &present) || !present) {
return false;
}
+ if (!handler.getMaxContentBoost(&metadata->maxContentBoost, &present) || !present) {
+ return false;
+ }
+ if (!handler.getHdrCapacityMax(&metadata->hdrCapacityMax, &present) || !present) {
+ return false;
+ }
+ if (!handler.getMinContentBoost(&metadata->minContentBoost, &present)) {
+ if (present) return false;
+ metadata->minContentBoost = 1.0f;
+ }
+ if (!handler.getGamma(&metadata->gamma, &present)) {
+ if (present) return false;
+ metadata->gamma = 1.0f;
+ }
+ if (!handler.getOffsetSdr(&metadata->offsetSdr, &present)) {
+ if (present) return false;
+ metadata->offsetSdr = 1.0f / 64.0f;
+ }
+ if (!handler.getOffsetHdr(&metadata->offsetHdr, &present)) {
+ if (present) return false;
+ metadata->offsetHdr = 1.0f / 64.0f;
+ }
+ if (!handler.getHdrCapacityMin(&metadata->hdrCapacityMin, &present)) {
+ if (present) return false;
+ metadata->hdrCapacityMin = 1.0f;
+ }
- if (!handler.getMinContentBoost(&metadata->minContentBoost)) {
+ bool base_rendition_is_hdr;
+ if (!handler.getBaseRenditionIsHdr(&base_rendition_is_hdr, &present)) {
+ if (present) return false;
+ base_rendition_is_hdr = false;
+ }
+ if (base_rendition_is_hdr) {
+ ALOGE("Base rendition of HDR is not supported!");
return false;
}
@@ -355,12 +586,11 @@
writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.minContentBoost));
writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.maxContentBoost));
- writer.WriteAttributeNameAndValue(kMapGamma, "1");
- writer.WriteAttributeNameAndValue(kMapOffsetSdr, "0");
- writer.WriteAttributeNameAndValue(kMapOffsetHdr, "0");
- writer.WriteAttributeNameAndValue(
- kMapHDRCapacityMin, std::max(log2(metadata.minContentBoost), 0.0f));
- writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, log2(metadata.maxContentBoost));
+ writer.WriteAttributeNameAndValue(kMapGamma, metadata.gamma);
+ writer.WriteAttributeNameAndValue(kMapOffsetSdr, metadata.offsetSdr);
+ writer.WriteAttributeNameAndValue(kMapOffsetHdr, metadata.offsetHdr);
+ writer.WriteAttributeNameAndValue(kMapHDRCapacityMin, log2(metadata.hdrCapacityMin));
+ writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, log2(metadata.hdrCapacityMax));
writer.WriteAttributeNameAndValue(kMapBaseRenditionIsHDR, "False");
writer.FinishWriting();
diff --git a/libs/ultrahdr/tests/data/minnie-320x240-yuv-icc.jpg b/libs/ultrahdr/tests/data/minnie-320x240-yuv-icc.jpg
index f61e0e8..c7f4538 100644
--- a/libs/ultrahdr/tests/data/minnie-320x240-yuv-icc.jpg
+++ b/libs/ultrahdr/tests/data/minnie-320x240-yuv-icc.jpg
Binary files differ
diff --git a/libs/ultrahdr/tests/jpegr_test.cpp b/libs/ultrahdr/tests/jpegr_test.cpp
index d482ea1..41d55ec 100644
--- a/libs/ultrahdr/tests/jpegr_test.cpp
+++ b/libs/ultrahdr/tests/jpegr_test.cpp
@@ -819,6 +819,52 @@
EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
&jpegR, nullptr, nullptr, &jpegR)) << "fail, API allows nullptr gainmap image";
+ // test metadata
+ ultrahdr_metadata_struct good_metadata;
+ good_metadata.version = "1.0";
+ good_metadata.minContentBoost = 1.0f;
+ good_metadata.maxContentBoost = 2.0f;
+ good_metadata.gamma = 1.0f;
+ good_metadata.offsetSdr = 0.0f;
+ good_metadata.offsetHdr = 0.0f;
+ good_metadata.hdrCapacityMin = 1.0f;
+ good_metadata.hdrCapacityMax = 2.0f;
+
+ ultrahdr_metadata_struct metadata = good_metadata;
+ metadata.version = "1.1";
+ EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
+ &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata version";
+
+ metadata = good_metadata;
+ metadata.minContentBoost = 3.0f;
+ EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
+ &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata content boost";
+
+ metadata = good_metadata;
+ metadata.gamma = -0.1f;
+ EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
+ &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata gamma";
+
+ metadata = good_metadata;
+ metadata.offsetSdr = -0.1f;
+ EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
+ &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata offset sdr";
+
+ metadata = good_metadata;
+ metadata.offsetHdr = -0.1f;
+ EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
+ &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata offset hdr";
+
+ metadata = good_metadata;
+ metadata.hdrCapacityMax = 0.5f;
+ EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
+ &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata hdr capacity max";
+
+ metadata = good_metadata;
+ metadata.hdrCapacityMin = 0.5f;
+ EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
+ &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata hdr capacity min";
+
free(jpegR.data);
}
@@ -864,8 +910,13 @@
TEST_F(JpegRTest, writeXmpThenRead) {
ultrahdr_metadata_struct metadata_expected;
metadata_expected.version = "1.0";
- metadata_expected.maxContentBoost = 1.25;
- metadata_expected.minContentBoost = 0.75;
+ metadata_expected.maxContentBoost = 1.25f;
+ metadata_expected.minContentBoost = 0.75f;
+ metadata_expected.gamma = 1.0f;
+ metadata_expected.offsetSdr = 0.0f;
+ metadata_expected.offsetHdr = 0.0f;
+ metadata_expected.hdrCapacityMin = 1.0f;
+ metadata_expected.hdrCapacityMax = metadata_expected.maxContentBoost;
const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
@@ -882,6 +933,11 @@
EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
EXPECT_FLOAT_EQ(metadata_expected.maxContentBoost, metadata_read.maxContentBoost);
EXPECT_FLOAT_EQ(metadata_expected.minContentBoost, metadata_read.minContentBoost);
+ EXPECT_FLOAT_EQ(metadata_expected.gamma, metadata_read.gamma);
+ EXPECT_FLOAT_EQ(metadata_expected.offsetSdr, metadata_read.offsetSdr);
+ EXPECT_FLOAT_EQ(metadata_expected.offsetHdr, metadata_read.offsetHdr);
+ EXPECT_FLOAT_EQ(metadata_expected.hdrCapacityMin, metadata_read.hdrCapacityMin);
+ EXPECT_FLOAT_EQ(metadata_expected.hdrCapacityMax, metadata_read.hdrCapacityMax);
}
/* Test Encode API-0 */
@@ -1297,9 +1353,7 @@
JpegRBenchmark benchmark;
- ultrahdr_metadata_struct metadata = { .version = "1.0",
- .maxContentBoost = 8.0f,
- .minContentBoost = 1.0f / 8.0f };
+ ultrahdr_metadata_struct metadata = { .version = "1.0" };
jpegr_uncompressed_struct map = { .data = NULL,
.width = 0,
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 32c21f6..c787fc9 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -699,7 +699,7 @@
#ifndef EGL_EXT_gl_colorspace_bt2020_hlg
#define EGL_EXT_gl_colorspace_bt2020_hlg 1
-#define EGL_GL_COLORSPACE_BT2020_HLG_EXT 0x333E
+#define EGL_GL_COLORSPACE_BT2020_HLG_EXT 0x3540
#endif /* EGL_EXT_gl_colorspace_bt2020_hlg */
#ifndef EGL_EXT_gl_colorspace_bt2020_linear
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 2c3ce16..b4fc5f0 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -137,21 +137,28 @@
#endif
#endif
-static const char* DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
+static const char* PERSIST_DRIVER_SUFFIX_PROPERTY = "persist.graphics.egl";
+static const char* RO_DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
+static const char* RO_BOARD_PLATFORM_PROPERTY = "ro.board.platform";
static const char* HAL_SUBNAME_KEY_PROPERTIES[3] = {
- "persist.graphics.egl",
- DRIVER_SUFFIX_PROPERTY,
- "ro.board.platform",
+ PERSIST_DRIVER_SUFFIX_PROPERTY,
+ RO_DRIVER_SUFFIX_PROPERTY,
+ RO_BOARD_PLATFORM_PROPERTY,
};
+// Check whether the loaded system drivers should be unloaded in order to
+// load ANGLE or the updatable graphics drivers.
+// If ANGLE namespace is set, it means the application is identified to run on top of ANGLE.
+// If updatable graphics driver namespace is set, it means the application is identified to
+// run on top of updatable graphics drivers.
static bool should_unload_system_driver(egl_connection_t* cnx) {
// Return false if the system driver has been unloaded once.
if (cnx->systemDriverUnloaded) {
return false;
}
- // Return true if Angle namespace is set.
+ // Return true if ANGLE namespace is set.
android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
if (ns) {
return true;
@@ -245,17 +252,20 @@
continue;
}
hnd = attempt_to_load_system_driver(cnx, prop.c_str(), true);
- if (hnd) {
- break;
- } else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) {
+ if (!hnd) {
+ ALOGD("Failed to load drivers from property %s with value %s", key, prop.c_str());
failToLoadFromDriverSuffixProperty = true;
}
+
+ // Abort regardless of whether subsequent properties are set, the value must be set
+ // correctly with the first property that has a value.
+ break;
}
}
if (!hnd) {
- // Can't find graphics driver by appending system properties, now search for the exact name
- // without any suffix of the GLES userspace driver in both locations.
+ // Can't find graphics driver by appending the value from system properties, now search for
+ // the exact name without any suffix of the GLES userspace driver in both locations.
// i.e.:
// libGLES.so, or:
// libEGL.so, libGLESv1_CM.so, libGLESv2.so
@@ -274,10 +284,10 @@
false, systemTime() - openTime);
} else {
// init_angle_backend will check if loaded driver is ANGLE or not,
- // will set cnx->useAngle appropriately.
+ // will set cnx->angleLoaded appropriately.
// Do this here so that we use ANGLE path when driver is ANGLE (e.g. loaded as native),
// not just loading ANGLE as option.
- init_angle_backend(hnd->dso[2], cnx);
+ attempt_to_init_angle_backend(hnd->dso[2], cnx);
}
LOG_ALWAYS_FATAL_IF(!hnd,
@@ -319,7 +329,7 @@
delete hnd;
cnx->dso = nullptr;
- cnx->useAngle = false;
+ cnx->angleLoaded = false;
}
void Loader::init_api(void* dso,
@@ -560,14 +570,14 @@
return hnd;
}
-void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) {
+void Loader::attempt_to_init_angle_backend(void* dso, egl_connection_t* cnx) {
void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform");
if (pANGLEGetDisplayPlatform) {
- ALOGV("ANGLE GLES library in use");
- cnx->useAngle = true;
+ ALOGV("ANGLE GLES library loaded");
+ cnx->angleLoaded = true;
} else {
- ALOGV("Native GLES library in use");
- cnx->useAngle = false;
+ ALOGV("Native GLES library loaded");
+ cnx->angleLoaded = false;
}
}
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 81742ab..cadbd46 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -57,7 +57,7 @@
driver_t* attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix, const bool exact);
void unload_system_driver(egl_connection_t* cnx);
void initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask);
- void init_angle_backend(void* dso, egl_connection_t* cnx);
+ void attempt_to_init_angle_backend(void* dso, egl_connection_t* cnx);
static __attribute__((noinline)) void init_api(void* dso, const char* const* api,
const char* const* ref_api,
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 525fed1..3317347 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -191,7 +191,7 @@
if (cnx->dso) {
EGLDisplay dpy = EGL_NO_DISPLAY;
- if (cnx->useAngle) {
+ if (cnx->angleLoaded) {
EGLint error;
dpy = getPlatformDisplayAngle(display, cnx, attrib_list, &error);
if (error != EGL_NONE) {
@@ -324,7 +324,7 @@
// b/269060366 Conditionally enabled EGL_ANDROID_get_frame_timestamps extension if the
// device's present timestamps are reliable (which may not be the case on emulators).
- if (cnx->useAngle) {
+ if (cnx->angleLoaded) {
if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
mExtensionString.append("EGL_ANDROID_get_frame_timestamps ");
}
@@ -432,7 +432,7 @@
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
// If we're using ANGLE reset any custom DisplayPlatform
- if (cnx->useAngle) {
+ if (cnx->angleLoaded) {
angle::resetAnglePlatform(disp.dpy);
}
if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index efbe613..33a77c4 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -84,7 +84,7 @@
if (win != nullptr && connected) {
// NOTE: When using Vulkan backend, the Vulkan runtime makes all the
// native_window_* calls, so don't do them here.
- if (!cnx->useAngle) {
+ if (!cnx->angleLoaded) {
native_window_set_buffers_format(win, 0);
if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) {
ALOGW("EGLNativeWindowType %p disconnect failed", win);
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 48718bb..88001b2 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -680,7 +680,7 @@
// NOTE: When using Vulkan backend, the Vulkan runtime makes all the
// native_window_* calls, so don't do them here.
- if (!cnx->useAngle) {
+ if (!cnx->angleLoaded) {
int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
if (result < 0) {
ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
@@ -699,14 +699,14 @@
std::vector<AttrType> strippedAttribList;
if (!processAttributes<AttrType>(dp, window, attrib_list, &colorSpace, &strippedAttribList)) {
ALOGE("error invalid colorspace: %d", colorSpace);
- if (!cnx->useAngle) {
+ if (!cnx->angleLoaded) {
native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
}
return EGL_NO_SURFACE;
}
attrib_list = strippedAttribList.data();
- if (!cnx->useAngle) {
+ if (!cnx->angleLoaded) {
int err = native_window_set_buffers_format(window, static_cast<int>(format));
if (err != 0) {
ALOGE("error setting native window pixel format: %s (%d)", strerror(-err), err);
@@ -738,7 +738,7 @@
}
// EGLSurface creation failed
- if (!cnx->useAngle) {
+ if (!cnx->angleLoaded) {
native_window_set_buffers_format(window, 0);
native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
}
@@ -1349,7 +1349,7 @@
}
}
- if (!s->cnx->useAngle) {
+ if (!s->cnx->angleLoaded) {
if (!sendSurfaceMetadata(s)) {
native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL);
return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE);
@@ -1374,7 +1374,7 @@
androidRect.bottom = y;
androidRects.push_back(androidRect);
}
- if (!s->cnx->useAngle) {
+ if (!s->cnx->angleLoaded) {
native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(),
androidRects.size());
}
@@ -1465,7 +1465,7 @@
int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
if (err != 0) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- } else if (!s->cnx->useAngle) {
+ } else if (!s->cnx->angleLoaded) {
return EGL_TRUE;
} // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below
}
@@ -1479,7 +1479,7 @@
int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
if (err != 0) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- } else if (!s->cnx->useAngle) {
+ } else if (!s->cnx->angleLoaded) {
return EGL_TRUE;
} // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below
}
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index fcc11f1..3bd37cb 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -41,7 +41,8 @@
libEgl(nullptr),
libGles1(nullptr),
libGles2(nullptr),
- systemDriverUnloaded(false) {
+ systemDriverUnloaded(false),
+ angleLoaded(false) {
const char* const* entries = platform_names;
EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform);
while (*entries) {
@@ -73,7 +74,7 @@
void* libGles2;
bool systemDriverUnloaded;
- bool useAngle; // Was ANGLE successfully loaded
+ bool angleLoaded; // Was ANGLE successfully loaded
};
extern gl_hooks_t gHooks[2];
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index ffcc967..dc7c75a 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -106,6 +106,13 @@
"libstatspull",
"libstatssocket",
],
+ include_dirs: [
+ "bionic/libc/kernel/android/uapi/",
+ "bionic/libc/kernel/uapi",
+ ],
+ cflags: [
+ "-D__ANDROID_HOST__",
+ ],
},
},
}
@@ -230,6 +237,9 @@
"inputflinger",
"libinputflingerhost",
+ // rust targets
+ "libinput_rust_test",
+
// native fuzzers
"inputflinger_latencytracker_fuzzer",
"inputflinger_cursor_input_fuzzer",
diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp
index 7812880..6ccd9e7 100644
--- a/services/inputflinger/InputCommonConverter.cpp
+++ b/services/inputflinger/InputCommonConverter.cpp
@@ -289,9 +289,9 @@
static void getHalPropertiesAndCoords(const NotifyMotionArgs& args,
std::vector<common::PointerProperties>& outPointerProperties,
std::vector<common::PointerCoords>& outPointerCoords) {
- outPointerProperties.reserve(args.pointerCount);
- outPointerCoords.reserve(args.pointerCount);
- for (size_t i = 0; i < args.pointerCount; i++) {
+ outPointerProperties.reserve(args.getPointerCount());
+ outPointerCoords.reserve(args.getPointerCount());
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
common::PointerProperties properties;
properties.id = args.pointerProperties[i].id;
properties.toolType = getToolType(args.pointerProperties[i].toolType);
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index f92c4f4..89e37db 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -40,6 +40,9 @@
const bool DEBUG = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus) {
+ // When adding cases to this switch, also add them to the copy of this method in
+ // TouchpadInputMapper.cpp.
+ // TODO(b/286394420): deduplicate this method with the one in TouchpadInputMapper.cpp.
switch (linuxBus) {
case BUS_USB:
return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USB;
@@ -78,10 +81,11 @@
std::vector<int32_t> uids;
std::vector<int32_t> durationsPerUid;
for (auto& [uid, dur] : report.uidBreakdown) {
- uids.push_back(uid);
+ uids.push_back(uid.val());
int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
durationsPerUid.push_back(durMillis);
- ALOGD_IF(DEBUG, " - uid: %d\t duration: %dms", uid, durMillis);
+ ALOGD_IF(DEBUG, " - uid: %s\t duration: %dms", uid.toString().c_str(),
+ durMillis);
}
util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, identifier.vendor, identifier.product,
identifier.version, linuxBusToInputDeviceBusEnum(identifier.bus),
@@ -125,10 +129,10 @@
}
std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
- LOG_ALWAYS_FATAL_IF(motionArgs.pointerCount < 1, "Received motion args without pointers");
+ LOG_ALWAYS_FATAL_IF(motionArgs.getPointerCount() < 1, "Received motion args without pointers");
std::set<InputDeviceUsageSource> sources;
- for (uint32_t i = 0; i < motionArgs.pointerCount; i++) {
+ for (uint32_t i = 0; i < motionArgs.getPointerCount(); i++) {
const auto toolType = motionArgs.pointerProperties[i].toolType;
if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
if (toolType == ToolType::MOUSE) {
@@ -257,12 +261,8 @@
}
void InputDeviceMetricsCollector::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
- const std::set<int32_t>& uids) {
- std::set<Uid> typeSafeUids;
- for (auto uid : uids) {
- typeSafeUids.emplace(uid);
- }
- mInteractionsQueue.push(DeviceId{deviceId}, timestamp, typeSafeUids);
+ const std::set<Uid>& uids) {
+ mInteractionsQueue.push(DeviceId{deviceId}, timestamp, uids);
}
void InputDeviceMetricsCollector::dump(std::string& dump) {
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index 387786f..c70e6d4 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -21,6 +21,7 @@
#include "SyncQueue.h"
#include <ftl/mixins.h>
+#include <gui/WindowInfo.h>
#include <input/InputDevice.h>
#include <statslog.h>
#include <chrono>
@@ -43,7 +44,7 @@
* Called from the InputDispatcher thread.
*/
virtual void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
- const std::set<int32_t>& uids) = 0;
+ const std::set<gui::Uid>& uids) = 0;
/**
* Dump the state of the interaction blocker.
* This method may be called on any thread (usually by the input manager on a binder thread).
@@ -101,7 +102,7 @@
// Describes the breakdown of an input device usage session by the UIDs that it interacted with.
using UidUsageBreakdown =
- std::vector<std::pair<int32_t /*uid*/, std::chrono::nanoseconds /*duration*/>>;
+ std::vector<std::pair<gui::Uid, std::chrono::nanoseconds /*duration*/>>;
struct DeviceUsageReport {
std::chrono::nanoseconds usageDuration;
@@ -134,7 +135,7 @@
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
- const std::set<int32_t>& uids) override;
+ const std::set<gui::Uid>& uids) override;
void dump(std::string& dump) override;
private:
@@ -152,13 +153,7 @@
return std::to_string(ftl::to_underlying(id));
}
- // Type-safe wrapper for a UID.
- struct Uid : ftl::Constructible<Uid, std::int32_t>, ftl::Equatable<Uid>, ftl::Orderable<Uid> {
- using Constructible::Constructible;
- };
- static inline std::string toString(const Uid& src) {
- return std::to_string(ftl::to_underlying(src));
- }
+ using Uid = gui::Uid;
std::map<DeviceId, InputDeviceInfo> mLoggedDeviceInfos;
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
index 408fbed..0fa47d1 100644
--- a/services/inputflinger/NotifyArgs.cpp
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -83,7 +83,6 @@
buttonState(buttonState),
classification(classification),
edgeFlags(edgeFlags),
- pointerCount(pointerCount),
xPrecision(xPrecision),
yPrecision(yPrecision),
xCursorPosition(xCursorPosition),
@@ -92,36 +91,8 @@
readTime(readTime),
videoFrames(videoFrames) {
for (uint32_t i = 0; i < pointerCount; i++) {
- this->pointerProperties[i].copyFrom(pointerProperties[i]);
- this->pointerCoords[i].copyFrom(pointerCoords[i]);
- }
-}
-
-NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other)
- : id(other.id),
- eventTime(other.eventTime),
- deviceId(other.deviceId),
- source(other.source),
- displayId(other.displayId),
- policyFlags(other.policyFlags),
- action(other.action),
- actionButton(other.actionButton),
- flags(other.flags),
- metaState(other.metaState),
- buttonState(other.buttonState),
- classification(other.classification),
- edgeFlags(other.edgeFlags),
- pointerCount(other.pointerCount),
- xPrecision(other.xPrecision),
- yPrecision(other.yPrecision),
- xCursorPosition(other.xCursorPosition),
- yCursorPosition(other.yCursorPosition),
- downTime(other.downTime),
- readTime(other.readTime),
- videoFrames(other.videoFrames) {
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointerProperties[i].copyFrom(other.pointerProperties[i]);
- pointerCoords[i].copyFrom(other.pointerCoords[i]);
+ this->pointerProperties.push_back(pointerProperties[i]);
+ this->pointerCoords.push_back(pointerCoords[i]);
}
}
@@ -130,35 +101,22 @@
}
bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
- bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+ return id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
policyFlags == rhs.policyFlags && action == rhs.action &&
actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState &&
buttonState == rhs.buttonState && classification == rhs.classification &&
- edgeFlags == rhs.edgeFlags &&
- pointerCount == rhs.pointerCount
- // PointerProperties and PointerCoords are compared separately below
- && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
+ edgeFlags == rhs.edgeFlags && pointerProperties == rhs.pointerProperties &&
+ pointerCoords == rhs.pointerCoords && xPrecision == rhs.xPrecision &&
+ yPrecision == rhs.yPrecision &&
isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) &&
isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) &&
downTime == rhs.downTime && videoFrames == rhs.videoFrames;
- if (!equal) {
- return false;
- }
-
- for (size_t i = 0; i < pointerCount; i++) {
- equal = pointerProperties[i] == rhs.pointerProperties[i] &&
- pointerCoords[i] == rhs.pointerCoords[i];
- if (!equal) {
- return false;
- }
- }
- return true;
}
std::string NotifyMotionArgs::dump() const {
std::string coords;
- for (uint32_t i = 0; i < pointerCount; i++) {
+ for (uint32_t i = 0; i < getPointerCount(); i++) {
if (!coords.empty()) {
coords += ", ";
}
@@ -181,11 +139,10 @@
coords += "}";
}
return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
- ", source=%s, action=%s, pointerCount=%" PRIu32
- " pointers=%s, flags=0x%08x)",
+ ", source=%s, action=%s, pointerCount=%zu pointers=%s, flags=0x%08x)",
id, eventTime, deviceId, inputEventSourceToString(source).c_str(),
- MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(),
- flags);
+ MotionEvent::actionToString(action).c_str(), getPointerCount(),
+ coords.c_str(), flags);
}
// --- NotifySwitchArgs ---
diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
index fbd296c..ee0ab33 100644
--- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp
+++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
@@ -22,7 +22,7 @@
static std::pair<bool, bool> checkToolType(const NotifyMotionArgs& args) {
bool hasStylus = false;
bool hasTouch = false;
- for (size_t i = 0; i < args.pointerCount; i++) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
// Make sure we are canceling stylus pointers
const ToolType toolType = args.pointerProperties[i].toolType;
if (isStylusToolType(toolType)) {
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 02bc47d..f889de5 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -117,7 +117,7 @@
}
static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) {
- for (size_t i = 0; i < args.pointerCount; i++) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
if (pointerId == args.pointerProperties[i].id) {
return AMOTION_EVENT_ACTION_POINTER_UP |
(i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
@@ -156,9 +156,10 @@
actionMasked == AMOTION_EVENT_ACTION_POINTER_UP;
NotifyMotionArgs newArgs{args};
- newArgs.pointerCount = 0;
+ newArgs.pointerProperties.clear();
+ newArgs.pointerCoords.clear();
int32_t newActionIndex = 0;
- for (uint32_t i = 0; i < args.pointerCount; i++) {
+ for (uint32_t i = 0; i < args.getPointerCount(); i++) {
const int32_t pointerId = args.pointerProperties[i].id;
if (pointerIds.find(pointerId) != pointerIds.end()) {
// skip this pointer
@@ -170,19 +171,18 @@
}
continue;
}
- newArgs.pointerProperties[newArgs.pointerCount].copyFrom(args.pointerProperties[i]);
- newArgs.pointerCoords[newArgs.pointerCount].copyFrom(args.pointerCoords[i]);
+ newArgs.pointerProperties.push_back(args.pointerProperties[i]);
+ newArgs.pointerCoords.push_back(args.pointerCoords[i]);
if (i == actionIndex) {
- newActionIndex = newArgs.pointerCount;
+ newActionIndex = newArgs.getPointerCount() - 1;
}
- newArgs.pointerCount++;
}
// Update POINTER_DOWN or POINTER_UP actions
if (isPointerUpOrDownAction && newArgs.action != ACTION_UNKNOWN) {
newArgs.action =
actionMasked | (newActionIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
// Convert POINTER_DOWN and POINTER_UP to DOWN and UP if there's only 1 pointer remaining
- if (newArgs.pointerCount == 1) {
+ if (newArgs.getPointerCount() == 1) {
if (actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) {
newArgs.action = AMOTION_EVENT_ACTION_DOWN;
} else if (actionMasked == AMOTION_EVENT_ACTION_POINTER_UP) {
@@ -201,13 +201,14 @@
*/
static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) {
std::set<int32_t> stylusPointerIds;
- for (uint32_t i = 0; i < args.pointerCount; i++) {
+ for (uint32_t i = 0; i < args.getPointerCount(); i++) {
if (isStylusToolType(args.pointerProperties[i].toolType)) {
stylusPointerIds.insert(args.pointerProperties[i].id);
}
}
NotifyMotionArgs withoutStylusPointers = removePointerIds(args, stylusPointerIds);
- if (withoutStylusPointers.pointerCount == 0 || withoutStylusPointers.action == ACTION_UNKNOWN) {
+ if (withoutStylusPointers.getPointerCount() == 0 ||
+ withoutStylusPointers.action == ACTION_UNKNOWN) {
return std::nullopt;
}
return withoutStylusPointers;
@@ -272,7 +273,7 @@
std::vector<NotifyMotionArgs> cancelSuppressedPointers(
const NotifyMotionArgs& args, const std::set<int32_t>& oldSuppressedPointerIds,
const std::set<int32_t>& newSuppressedPointerIds) {
- LOG_ALWAYS_FATAL_IF(args.pointerCount == 0, "0 pointers in %s", args.dump().c_str());
+ LOG_ALWAYS_FATAL_IF(args.getPointerCount() == 0, "0 pointers in %s", args.dump().c_str());
// First, let's remove the old suppressed pointers. They've already been canceled previously.
NotifyMotionArgs oldArgs = removePointerIds(args, oldSuppressedPointerIds);
@@ -284,7 +285,7 @@
const int32_t actionMasked = MotionEvent::getActionMasked(args.action);
// We will iteratively remove pointers from 'removedArgs'.
NotifyMotionArgs removedArgs{oldArgs};
- for (uint32_t i = 0; i < oldArgs.pointerCount; i++) {
+ for (uint32_t i = 0; i < oldArgs.getPointerCount(); i++) {
const int32_t pointerId = oldArgs.pointerProperties[i].id;
if (newSuppressedPointerIds.find(pointerId) == newSuppressedPointerIds.end()) {
// This is a pointer that should not be canceled. Move on.
@@ -296,7 +297,7 @@
continue;
}
- if (removedArgs.pointerCount == 1) {
+ if (removedArgs.getPointerCount() == 1) {
// We are about to remove the last pointer, which means there will be no more gesture
// remaining. This is identical to canceling all pointers, so just send a single CANCEL
// event, without any of the preceding POINTER_UP with FLAG_CANCELED events.
@@ -314,7 +315,7 @@
}
// Now 'removedArgs' contains only pointers that are valid.
- if (removedArgs.pointerCount <= 0 || removedArgs.action == ACTION_UNKNOWN) {
+ if (removedArgs.getPointerCount() <= 0 || removedArgs.action == ACTION_UNKNOWN) {
return out;
}
out.push_back(removedArgs);
@@ -473,7 +474,7 @@
UnwantedInteractionBlocker::~UnwantedInteractionBlocker() {}
void SlotState::update(const NotifyMotionArgs& args) {
- for (size_t i = 0; i < args.pointerCount; i++) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
const int32_t pointerId = args.pointerProperties[i].id;
const int32_t resolvedAction = resolveActionForPointer(i, args.action);
processPointerId(pointerId, resolvedAction);
@@ -571,7 +572,7 @@
const SlotState& newSlotState) {
std::vector<::ui::InProgressTouchEvdev> touches;
- for (size_t i = 0; i < args.pointerCount; i++) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
const int32_t pointerId = args.pointerProperties[i].id;
touches.emplace_back(::ui::InProgressTouchEvdev());
touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
@@ -660,7 +661,7 @@
// Now that we know which slots should be suppressed, let's convert those to pointer id's.
std::set<int32_t> newSuppressedIds;
- for (size_t i = 0; i < args.pointerCount; i++) {
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
const int32_t pointerId = args.pointerProperties[i].id;
std::optional<size_t> slot = oldSlotState.getSlotForPointerId(pointerId);
if (!slot) {
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 06a7352..b2e274d 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -36,8 +36,8 @@
constexpr int32_t DEVICE_ID = 1;
// The default pid and uid for windows created by the test.
-constexpr int32_t WINDOW_PID = 999;
-constexpr int32_t WINDOW_UID = 1001;
+constexpr gui::Pid WINDOW_PID{999};
+constexpr gui::Uid WINDOW_UID{1001};
static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s;
static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
@@ -61,13 +61,13 @@
ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
}
- void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<int32_t> pid,
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
const std::string& reason) override {
ALOGE("Window is not responding: %s", reason.c_str());
}
void notifyWindowResponsive(const sp<IBinder>& connectionToken,
- std::optional<int32_t> pid) override {}
+ std::optional<gui::Pid> pid) override {}
void notifyInputChannelBroken(const sp<IBinder>&) override {}
@@ -112,7 +112,7 @@
void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
- const std::set<int32_t>& uids) override {}
+ const std::set<gui::Uid>& uids) override {}
InputDispatcherConfiguration mConfig;
};
diff --git a/services/inputflinger/dispatcher/DebugConfig.cpp b/services/inputflinger/dispatcher/DebugConfig.cpp
index 764194d..12122fd 100644
--- a/services/inputflinger/dispatcher/DebugConfig.cpp
+++ b/services/inputflinger/dispatcher/DebugConfig.cpp
@@ -30,11 +30,10 @@
bool debugInboundEventDetails() {
if (!IS_DEBUGGABLE_BUILD) {
static const bool DEBUG_INBOUND_EVENT_DETAILS =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent",
- ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "InboundEvent");
return DEBUG_INBOUND_EVENT_DETAILS;
}
- return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", ANDROID_LOG_INFO);
+ return android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "InboundEvent");
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h
index 0e260a7..7a41d68 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -18,8 +18,7 @@
#define LOG_TAG "InputDispatcher"
-#include <log/log.h>
-#include <log/log_event_list.h>
+#include <android-base/logging.h>
namespace android::inputdispatcher {
@@ -42,14 +41,14 @@
* Enable this via "adb shell setprop log.tag.InputDispatcherOutboundEvent DEBUG" (requires restart)
*/
const bool DEBUG_OUTBOUND_EVENT_DETAILS =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "OutboundEvent", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "OutboundEvent");
/**
* Log debug messages about the dispatch cycle.
* Enable this via "adb shell setprop log.tag.InputDispatcherDispatchCycle DEBUG" (requires restart)
*/
const bool DEBUG_DISPATCH_CYCLE =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "DispatchCycle", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "DispatchCycle");
/**
* Log debug messages about channel creation
@@ -57,28 +56,28 @@
* restart)
*/
const bool DEBUG_CHANNEL_CREATION =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "ChannelCreation", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "ChannelCreation");
/**
* Log debug messages about input event injection.
* Enable this via "adb shell setprop log.tag.InputDispatcherInjection DEBUG" (requires restart)
*/
const bool DEBUG_INJECTION =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Injection", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "Injection");
/**
* Log debug messages about input focus tracking.
* Enable this via "adb shell setprop log.tag.InputDispatcherFocus DEBUG" (requires restart)
*/
const bool DEBUG_FOCUS =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Focus", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "Focus");
/**
* Log debug messages about touch mode event
* Enable this via "adb shell setprop log.tag.InputDispatcherTouchMode DEBUG" (requires restart)
*/
const bool DEBUG_TOUCH_MODE =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchMode", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "TouchMode");
/**
* Log debug messages about touch occlusion
@@ -90,13 +89,20 @@
* Enable this via "adb shell setprop log.tag.InputDispatcherAppSwitch DEBUG" (requires restart)
*/
const bool DEBUG_APP_SWITCH =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "AppSwitch", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "AppSwitch");
/**
* Log debug messages about hover events.
* Enable this via "adb shell setprop log.tag.InputDispatcherHover DEBUG" (requires restart)
*/
const bool DEBUG_HOVER =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Hover", ANDROID_LOG_INFO);
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "Hover");
+
+/**
+ * Crash if a bad stream from InputListener is detected.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherVerifyEvents DEBUG" (requires restart)
+ */
+const bool DEBUG_VERIFY_EVENTS =
+ android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "VerifyEvents");
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
index c2d3ad6..053594b 100644
--- a/services/inputflinger/dispatcher/InjectionState.cpp
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -20,7 +20,7 @@
namespace android::inputdispatcher {
-InjectionState::InjectionState(const std::optional<int32_t>& targetUid)
+InjectionState::InjectionState(const std::optional<gui::Uid>& targetUid)
: refCount(1),
targetUid(targetUid),
injectionResult(android::os::InputEventInjectionResult::PENDING),
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index d9e27ba..3a3f5ae 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -26,12 +26,12 @@
struct InjectionState {
mutable int32_t refCount;
- std::optional<int32_t> targetUid;
+ std::optional<gui::Uid> targetUid;
android::os::InputEventInjectionResult injectionResult; // initially PENDING
bool injectionIsAsync; // set to true if injection is not waiting for the result
int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
- explicit InjectionState(const std::optional<int32_t>& targetUid);
+ explicit InjectionState(const std::optional<gui::Uid>& targetUid);
void release();
private:
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 055fb6f..d91fe4c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -26,6 +26,7 @@
#include <android/os/IInputConstants.h>
#include <binder/Binder.h>
#include <ftl/enum.h>
+#include <log/log_event_list.h>
#if defined(__ANDROID__)
#include <gui/SurfaceComposerClient.h>
#endif
@@ -125,6 +126,10 @@
return StringPrintf("%p", binder.get());
}
+static std::string uidString(const gui::Uid& uid) {
+ return uid.toString();
+}
+
inline int32_t getMotionEventActionPointerIndex(int32_t action) {
return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
@@ -556,7 +561,7 @@
return !info.inputConfig.test(gui::WindowInfo::InputConfig::NOT_TOUCHABLE) && !info.isSpy();
}
-bool isWindowOwnedBy(const sp<WindowInfoHandle>& windowHandle, int32_t pid, int32_t uid) {
+bool isWindowOwnedBy(const sp<WindowInfoHandle>& windowHandle, gui::Pid pid, gui::Uid uid) {
if (windowHandle == nullptr) {
return false;
}
@@ -576,14 +581,16 @@
// The event was not injected, or the injected event does not target a window.
return {};
}
- const int32_t uid = *entry.injectionState->targetUid;
+ const auto uid = *entry.injectionState->targetUid;
if (window == nullptr) {
- return StringPrintf("No valid window target for injection into uid %d.", uid);
+ return StringPrintf("No valid window target for injection into uid %s.",
+ uid.toString().c_str());
}
if (entry.injectionState->targetUid != window->getInfo()->ownerUid) {
- return StringPrintf("Injected event targeted at uid %d would be dispatched to window '%s' "
- "owned by uid %d.",
- uid, window->getName().c_str(), window->getInfo()->ownerUid);
+ return StringPrintf("Injected event targeted at uid %s would be dispatched to window '%s' "
+ "owned by uid %s.",
+ uid.toString().c_str(), window->getName().c_str(),
+ window->getInfo()->ownerUid.toString().c_str());
}
return {};
}
@@ -647,7 +654,6 @@
TouchedWindow touchedWindow;
touchedWindow.windowHandle = oldWindow;
touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_HOVER_EXIT;
- touchedWindow.pointerIds.set(pointerId);
out.push_back(touchedWindow);
}
}
@@ -666,7 +672,7 @@
}
touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_IS;
}
- touchedWindow.pointerIds.set(pointerId);
+ touchedWindow.addHoveringPointer(entry.deviceId, pointerId);
if (canReceiveForegroundTouches(*newWindow->getInfo())) {
touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND;
}
@@ -2188,8 +2194,8 @@
/**
* In general, touch should be always split between windows. Some exceptions:
* 1. Don't split touch is if we have an active pointer down, and a new pointer is going down that's
- * from the same device, *and* the window that's receiving the current pointer does not support
- * split touch.
+ * from the same device, *and* the window that's receiving the current pointer does not support
+ * split touch.
* 2. Don't split mouse events
*/
bool InputDispatcher::shouldSplitTouch(const TouchState& touchState,
@@ -2214,7 +2220,8 @@
// Eventually, touchedWindow will contain the deviceId of each pointer that's currently
// being sent there. For now, use deviceId from touch state.
- if (entry.deviceId == touchState.deviceId && touchedWindow.pointerIds.any()) {
+ if (entry.deviceId == touchState.deviceId &&
+ touchedWindow.hasTouchingPointers(entry.deviceId)) {
return false;
}
}
@@ -2397,7 +2404,7 @@
// still add a window to the touch state. We should avoid doing that, but some of the
// later checks ("at least one foreground window") rely on this in order to dispatch
// the event properly, so that needs to be updated, possibly by looking at InputTargets.
- tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds,
+ tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, entry.deviceId, pointerIds,
isDownOrPointerDown
? std::make_optional(entry.eventTime)
: std::nullopt);
@@ -2421,8 +2428,8 @@
if (isSplit) {
wallpaperFlags |= InputTarget::Flags::SPLIT;
}
- tempTouchState.addOrUpdateWindow(wallpaper, wallpaperFlags, pointerIds,
- entry.eventTime);
+ tempTouchState.addOrUpdateWindow(wallpaper, wallpaperFlags, entry.deviceId,
+ pointerIds, entry.eventTime);
}
}
}
@@ -2433,12 +2440,12 @@
// which is a specific behaviour that we want.
const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
for (TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.pointerIds.test(pointerId) &&
- touchedWindow.pilferedPointerIds.count() > 0) {
+ if (touchedWindow.hasTouchingPointer(entry.deviceId, pointerId) &&
+ touchedWindow.hasPilferingPointers(entry.deviceId)) {
// This window is already pilfering some pointers, and this new pointer is also
// going to it. Therefore, take over this pointer and don't give it to anyone
// else.
- touchedWindow.pilferedPointerIds.set(pointerId);
+ touchedWindow.addPilferingPointer(entry.deviceId, pointerId);
}
}
@@ -2510,7 +2517,7 @@
tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
addWindowTargetLocked(oldTouchedWindowHandle,
InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds,
- touchedWindow.firstDownTimeInTarget, targets);
+ touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
// Make a slippery entrance into the new window.
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -2531,13 +2538,14 @@
targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
}
- tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds,
- entry.eventTime);
+ tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags,
+ entry.deviceId, pointerIds, entry.eventTime);
// Check if the wallpaper window should deliver the corresponding event.
slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
- tempTouchState, pointerId, targets);
- tempTouchState.removeTouchedPointerFromWindow(pointerId, oldTouchedWindowHandle);
+ tempTouchState, entry.deviceId, pointerId, targets);
+ tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointerId,
+ oldTouchedWindowHandle);
}
}
@@ -2551,7 +2559,8 @@
if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
continue;
}
- touchedWindow.pointerIds.set(entry.pointerProperties[pointerIndex].id);
+ touchedWindow.addTouchingPointer(entry.deviceId,
+ entry.pointerProperties[pointerIndex].id);
}
}
}
@@ -2563,7 +2572,7 @@
for (const TouchedWindow& touchedWindow : hoveringWindows) {
std::optional<InputTarget> target =
createInputTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.firstDownTimeInTarget);
+ touchedWindow.getDownTimeInTarget(entry.deviceId));
if (!target) {
continue;
}
@@ -2584,8 +2593,8 @@
}
if (!errs.empty()) {
ALOGW("Dropping targeted injection: At least one touched window is not owned by uid "
- "%d:%s",
- *entry.injectionState->targetUid, errs.c_str());
+ "%s:%s",
+ entry.injectionState->targetUid->toString().c_str(), errs.c_str());
outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
return {};
}
@@ -2597,7 +2606,7 @@
sp<WindowInfoHandle> foregroundWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle) {
- const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
+ const auto foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
for (InputTarget& target : targets) {
if (target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
sp<WindowInfoHandle> targetWindow =
@@ -2620,15 +2629,16 @@
// Output targets from the touch state.
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
+ if (!touchedWindow.hasTouchingPointers(entry.deviceId) &&
+ !touchedWindow.hasHoveringPointers(entry.deviceId)) {
// Windows with hovering pointers are getting persisted inside TouchState.
// Do not send this event to those windows.
continue;
}
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
- targets);
+ touchedWindow.getTouchingPointers(entry.deviceId),
+ touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
}
// During targeted injection, only allow owned targets to receive events
@@ -2687,7 +2697,7 @@
}
} else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
// Pointer went up.
- tempTouchState.removeTouchedPointer(entry.pointerProperties[0].id);
+ tempTouchState.removeTouchingPointer(entry.deviceId, entry.pointerProperties[0].id);
} else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
// All pointers up or canceled.
tempTouchState.reset();
@@ -2704,8 +2714,8 @@
for (size_t i = 0; i < tempTouchState.windows.size();) {
TouchedWindow& touchedWindow = tempTouchState.windows[i];
- touchedWindow.pointerIds.reset(pointerId);
- if (touchedWindow.pointerIds.none()) {
+ touchedWindow.removeTouchingPointer(entry.deviceId, pointerId);
+ if (!touchedWindow.hasTouchingPointers(entry.deviceId)) {
tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
continue;
}
@@ -2961,8 +2971,8 @@
TouchOcclusionInfo info;
info.hasBlockingOcclusion = false;
info.obscuringOpacity = 0;
- info.obscuringUid = -1;
- std::map<int32_t, float> opacityByUid;
+ info.obscuringUid = gui::Uid::INVALID;
+ std::map<gui::Uid, float> opacityByUid;
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
break; // All future windows are below us. Exit early.
@@ -2984,7 +2994,7 @@
break;
}
if (otherInfo->touchOcclusionMode == TouchOcclusionMode::USE_OPACITY) {
- uint32_t uid = otherInfo->ownerUid;
+ const auto uid = otherInfo->ownerUid;
float opacity =
(opacityByUid.find(uid) == opacityByUid.end()) ? 0 : opacityByUid[uid];
// Given windows A and B:
@@ -3008,29 +3018,30 @@
std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info,
bool isTouchedWindow) const {
- return StringPrintf(INDENT2 "* %spackage=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
+ return StringPrintf(INDENT2 "* %spackage=%s/%s, id=%" PRId32 ", mode=%s, alpha=%.2f, "
"frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
"], touchableRegion=%s, window={%s}, inputConfig={%s}, "
"hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
isTouchedWindow ? "[TOUCHED] " : "", info->packageName.c_str(),
- info->ownerUid, info->id, toString(info->touchOcclusionMode).c_str(),
- info->alpha, info->frameLeft, info->frameTop, info->frameRight,
- info->frameBottom, dumpRegion(info->touchableRegion).c_str(),
- info->name.c_str(), info->inputConfig.string().c_str(),
- toString(info->token != nullptr), info->applicationInfo.name.c_str(),
+ info->ownerUid.toString().c_str(), info->id,
+ toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
+ info->frameTop, info->frameRight, info->frameBottom,
+ dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
+ info->inputConfig.string().c_str(), toString(info->token != nullptr),
+ info->applicationInfo.name.c_str(),
binderToString(info->applicationInfo.token).c_str());
}
bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
if (occlusionInfo.hasBlockingOcclusion) {
- ALOGW("Untrusted touch due to occlusion by %s/%d", occlusionInfo.obscuringPackage.c_str(),
- occlusionInfo.obscuringUid);
+ ALOGW("Untrusted touch due to occlusion by %s/%s", occlusionInfo.obscuringPackage.c_str(),
+ occlusionInfo.obscuringUid.toString().c_str());
return false;
}
if (occlusionInfo.obscuringOpacity > mMaximumObscuringOpacityForTouch) {
- ALOGW("Untrusted touch due to occlusion by %s/%d (obscuring opacity = "
+ ALOGW("Untrusted touch due to occlusion by %s/%s (obscuring opacity = "
"%.2f, maximum allowed = %.2f)",
- occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid,
+ occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid.toString().c_str(),
occlusionInfo.obscuringOpacity, mMaximumObscuringOpacityForTouch);
return false;
}
@@ -3452,7 +3463,7 @@
return; // Not a key or a motion
}
- std::set<int32_t> interactionUids;
+ std::set<gui::Uid> interactionUids;
std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens;
std::vector<std::shared_ptr<Connection>> newConnections;
for (const InputTarget& target : targets) {
@@ -4324,7 +4335,7 @@
args.actionButton, args.flags, args.metaState, args.buttonState, args.edgeFlags,
args.xPrecision, args.yPrecision, args.xCursorPosition, args.yCursorPosition,
args.downTime);
- for (uint32_t i = 0; i < args.pointerCount; i++) {
+ for (uint32_t i = 0; i < args.getPointerCount(); i++) {
ALOGD(" Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, "
"touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",
i, args.pointerProperties[i].id,
@@ -4341,13 +4352,27 @@
}
}
- Result<void> motionCheck = validateMotionEvent(args.action, args.actionButton,
- args.pointerCount, args.pointerProperties);
+ Result<void> motionCheck =
+ validateMotionEvent(args.action, args.actionButton, args.getPointerCount(),
+ args.pointerProperties.data());
if (!motionCheck.ok()) {
LOG(ERROR) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();
return;
}
+ if (DEBUG_VERIFY_EVENTS) {
+ auto [it, _] =
+ mVerifiersByDisplay.try_emplace(args.displayId,
+ StringPrintf("display %" PRId32, args.displayId));
+ Result<void> result =
+ it->second.processMovement(args.deviceId, args.action, args.getPointerCount(),
+ args.pointerProperties.data(), args.pointerCoords.data(),
+ args.flags);
+ if (!result.ok()) {
+ LOG(FATAL) << "Bad stream: " << result.error() << " caused by " << args.dump();
+ }
+ }
+
uint32_t policyFlags = args.policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4387,8 +4412,8 @@
args.metaState, args.buttonState, args.classification,
displayTransform, args.xPrecision, args.yPrecision,
args.xCursorPosition, args.yCursorPosition, displayTransform,
- args.downTime, args.eventTime, args.pointerCount,
- args.pointerProperties, args.pointerCoords);
+ args.downTime, args.eventTime, args.getPointerCount(),
+ args.pointerProperties.data(), args.pointerCoords.data());
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy.filterInputEvent(event, policyFlags)) {
@@ -4406,8 +4431,9 @@
args.buttonState, args.classification, args.edgeFlags,
args.xPrecision, args.yPrecision,
args.xCursorPosition, args.yCursorPosition,
- args.downTime, args.pointerCount,
- args.pointerProperties, args.pointerCoords);
+ args.downTime, args.getPointerCount(),
+ args.pointerProperties.data(),
+ args.pointerCoords.data());
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
@@ -4516,7 +4542,7 @@
}
InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* event,
- std::optional<int32_t> targetUid,
+ std::optional<gui::Uid> targetUid,
InputEventInjectionSync syncMode,
std::chrono::milliseconds timeout,
uint32_t policyFlags) {
@@ -4527,7 +4553,7 @@
}
if (debugInboundEventDetails()) {
- LOG(DEBUG) << __func__ << ": targetUid=" << toString(targetUid)
+ LOG(DEBUG) << __func__ << ": targetUid=" << toString(targetUid, &uidString)
<< ", syncMode=" << ftl::enum_string(syncMode) << ", timeout=" << timeout.count()
<< "ms, policyFlags=0x" << std::hex << policyFlags << std::dec
<< ", event=" << *event;
@@ -4972,8 +4998,8 @@
ALOGD("%s", log.c_str());
}
}
- ALOGW("Dropping untrusted touch event due to %s/%d", occlusionInfo.obscuringPackage.c_str(),
- occlusionInfo.obscuringUid);
+ ALOGW("Dropping untrusted touch event due to %s/%s", occlusionInfo.obscuringPackage.c_str(),
+ occlusionInfo.obscuringUid.toString().c_str());
return false;
}
@@ -5335,15 +5361,16 @@
mLooper->wake();
}
-bool InputDispatcher::setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission,
- int32_t displayId) {
+bool InputDispatcher::setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid,
+ bool hasPermission, int32_t displayId) {
bool needWake = false;
{
std::scoped_lock lock(mLock);
ALOGD_IF(DEBUG_TOUCH_MODE,
- "Request to change touch mode to %s (calling pid=%d, uid=%d, "
+ "Request to change touch mode to %s (calling pid=%s, uid=%s, "
"hasPermission=%s, target displayId=%d, mTouchModePerDisplay[displayId]=%s)",
- toString(inTouchMode), pid, uid, toString(hasPermission), displayId,
+ toString(inTouchMode), pid.toString().c_str(), uid.toString().c_str(),
+ toString(hasPermission), displayId,
mTouchModePerDisplay.count(displayId) == 0
? "not set"
: std::to_string(mTouchModePerDisplay[displayId]).c_str());
@@ -5355,9 +5382,9 @@
if (!hasPermission) {
if (!focusedWindowIsOwnedByLocked(pid, uid) &&
!recentWindowsAreOwnedByLocked(pid, uid)) {
- ALOGD("Touch mode switch rejected, caller (pid=%d, uid=%d) doesn't own the focused "
+ ALOGD("Touch mode switch rejected, caller (pid=%s, uid=%s) doesn't own the focused "
"window nor none of the previously interacted window",
- pid, uid);
+ pid.toString().c_str(), uid.toString().c_str());
return false;
}
}
@@ -5373,7 +5400,7 @@
return true;
}
-bool InputDispatcher::focusedWindowIsOwnedByLocked(int32_t pid, int32_t uid) {
+bool InputDispatcher::focusedWindowIsOwnedByLocked(gui::Pid pid, gui::Uid uid) {
const sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (focusedToken == nullptr) {
return false;
@@ -5382,7 +5409,7 @@
return isWindowOwnedBy(windowHandle, pid, uid);
}
-bool InputDispatcher::recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) {
+bool InputDispatcher::recentWindowsAreOwnedByLocked(gui::Pid pid, gui::Uid uid) {
return std::find_if(mInteractionConnectionTokens.begin(), mInteractionConnectionTokens.end(),
[&](const sp<IBinder>& connectionToken) REQUIRES(mLock) {
const sp<WindowInfoHandle> windowHandle =
@@ -5427,14 +5454,22 @@
// Find the target touch state and touched window by fromToken.
auto [state, touchedWindow, displayId] = findTouchStateWindowAndDisplayLocked(fromToken);
+
if (state == nullptr || touchedWindow == nullptr) {
- ALOGD("Focus transfer failed because from window is not being touched.");
+ ALOGD("Touch transfer failed because from window is not being touched.");
return false;
}
+ std::set<int32_t> deviceIds = touchedWindow->getTouchingDeviceIds();
+ if (deviceIds.size() != 1) {
+ LOG(DEBUG) << "Can't transfer touch. Currently touching devices: " << dumpSet(deviceIds)
+ << " for window: " << touchedWindow->dump();
+ return false;
+ }
+ const int32_t deviceId = *deviceIds.begin();
sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
if (toWindowHandle == nullptr) {
- ALOGW("Cannot transfer focus because to window not found.");
+ ALOGW("Cannot transfer touch because to window not found.");
return false;
}
@@ -5446,7 +5481,7 @@
// Erase old window.
ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags;
- std::bitset<MAX_POINTER_ID + 1> pointerIds = touchedWindow->pointerIds;
+ std::bitset<MAX_POINTER_ID + 1> pointerIds = touchedWindow->getTouchingPointers(deviceId);
sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
state->removeWindowByToken(fromToken);
@@ -5457,7 +5492,8 @@
if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) {
newTargetFlags |= InputTarget::Flags::FOREGROUND;
}
- state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds, downTimeInTarget);
+ state->addOrUpdateWindow(toWindowHandle, newTargetFlags, deviceId, pointerIds,
+ downTimeInTarget);
// Store the dragging window.
if (isDragDrop) {
@@ -5476,16 +5512,15 @@
std::shared_ptr<Connection> toConnection = getConnectionLocked(toToken);
if (fromConnection != nullptr && toConnection != nullptr) {
fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
- CancelationOptions
- options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "transferring touch focus from this window to another window");
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ "transferring touch from this window to another window");
synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection,
newTargetFlags);
// Check if the wallpaper window should deliver the corresponding event.
transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle,
- *state, pointerIds);
+ *state, deviceId, pointerIds);
}
} // release lock
@@ -5667,10 +5702,11 @@
windowInfo->applicationInfo.name.c_str(),
binderToString(windowInfo->applicationInfo.token).c_str());
dump += dumpRegion(windowInfo->touchableRegion);
- dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
+ dump += StringPrintf(", ownerPid=%s, ownerUid=%s, dispatchingTimeout=%" PRId64
"ms, hasToken=%s, "
"touchOcclusionMode=%s\n",
- windowInfo->ownerPid, windowInfo->ownerUid,
+ windowInfo->ownerPid.toString().c_str(),
+ windowInfo->ownerUid.toString().c_str(),
millis(windowInfo->dispatchingTimeout),
binderToString(windowInfo->token).c_str(),
toString(windowInfo->touchOcclusionMode).c_str());
@@ -5862,7 +5898,7 @@
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_t displayId,
const std::string& name,
- int32_t pid) {
+ gui::Pid pid) {
std::shared_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
status_t result = openInputChannelPair(name, serverChannel, clientChannel);
@@ -5968,20 +6004,28 @@
}
auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token);
- if (statePtr == nullptr || windowPtr == nullptr || windowPtr->pointerIds.none()) {
+ if (statePtr == nullptr || windowPtr == nullptr) {
ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams."
" Ignoring.");
return BAD_VALUE;
}
+ std::set<int32_t> deviceIds = windowPtr->getTouchingDeviceIds();
+ if (deviceIds.size() != 1) {
+ LOG(WARNING) << "Can't pilfer. Currently touching devices: " << dumpSet(deviceIds)
+ << " in window: " << windowPtr->dump();
+ return BAD_VALUE;
+ }
+ const int32_t deviceId = *deviceIds.begin();
TouchState& state = *statePtr;
TouchedWindow& window = *windowPtr;
// Send cancel events to all the input channels we're stealing from.
CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"input channel stole pointer stream");
- options.deviceId = state.deviceId;
+ options.deviceId = deviceId;
options.displayId = displayId;
- options.pointerIds = window.pointerIds;
+ std::bitset<MAX_POINTER_ID + 1> pointerIds = window.getTouchingPointers(deviceId);
+ options.pointerIds = pointerIds;
std::string canceledWindows;
for (const TouchedWindow& w : state.windows) {
const std::shared_ptr<InputChannel> channel =
@@ -5998,9 +6042,9 @@
// Prevent the gesture from being sent to any other windows.
// This only blocks relevant pointers to be sent to other windows
- window.pilferedPointerIds |= window.pointerIds;
+ window.addPilferingPointers(deviceId, pointerIds);
- state.cancelPointersForWindowsExcept(window.pointerIds, token);
+ state.cancelPointersForWindowsExcept(deviceId, pointerIds, token);
return OK;
}
@@ -6054,7 +6098,7 @@
} // release lock
}
-std::optional<int32_t> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) {
+std::optional<gui::Pid> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) {
for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) {
for (const Monitor& monitor : monitors) {
if (monitor.inputChannel->getConnectionToken() == token) {
@@ -6274,7 +6318,7 @@
}
void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token,
- std::optional<int32_t> pid,
+ std::optional<gui::Pid> pid,
std::string reason) {
auto command = [this, token, pid, reason = std::move(reason)]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
@@ -6284,7 +6328,7 @@
}
void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& token,
- std::optional<int32_t> pid) {
+ std::optional<gui::Pid> pid) {
auto command = [this, token, pid]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
mPolicy.notifyWindowResponsive(token, pid);
@@ -6300,7 +6344,7 @@
void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,
std::string reason) {
const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
- std::optional<int32_t> pid;
+ std::optional<gui::Pid> pid;
if (connection.monitor) {
ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
reason.c_str());
@@ -6322,7 +6366,7 @@
*/
void InputDispatcher::processConnectionResponsiveLocked(const Connection& connection) {
const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
- std::optional<int32_t> pid;
+ std::optional<gui::Pid> pid;
if (connection.monitor) {
pid = findMonitorPidByTokenLocked(connectionToken);
} else {
@@ -6573,7 +6617,7 @@
* this method can be safely called from any thread, as long as you've ensured that
* the work you are interested in completing has already been queued.
*/
-bool InputDispatcher::waitForIdle() {
+bool InputDispatcher::waitForIdle() const {
/**
* Timeout should represent the longest possible time that a device might spend processing
* events and commands.
@@ -6692,6 +6736,7 @@
std::erase(mIneligibleDisplaysForPointerCapture, displayId);
// Remove the associated touch mode state.
mTouchModePerDisplay.erase(displayId);
+ mVerifiersByDisplay.erase(displayId);
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -6786,7 +6831,7 @@
void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t pointerId,
+ TouchState& state, int32_t deviceId, int32_t pointerId,
std::vector<InputTarget>& targets) const {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
@@ -6808,8 +6853,8 @@
addWindowTargetLocked(oldWallpaper,
oldTouchedWindow.targetFlags |
InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
- pointerIds, oldTouchedWindow.firstDownTimeInTarget, targets);
- state.removeTouchedPointerFromWindow(pointerId, oldWallpaper);
+ pointerIds, oldTouchedWindow.getDownTimeInTarget(deviceId), targets);
+ state.removeTouchingPointerFromWindow(deviceId, pointerId, oldWallpaper);
}
if (newWallpaper != nullptr) {
@@ -6817,7 +6862,7 @@
InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER |
InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED,
- pointerIds);
+ deviceId, pointerIds);
}
}
@@ -6825,7 +6870,7 @@
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<WindowInfoHandle> fromWindowHandle,
const sp<WindowInfoHandle> toWindowHandle,
- TouchState& state,
+ TouchState& state, int32_t deviceId,
std::bitset<MAX_POINTER_ID + 1> pointerIds) {
const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) &&
fromWindowHandle->getInfo()->inputConfig.test(
@@ -6855,7 +6900,8 @@
oldTargetFlags & (InputTarget::Flags::SPLIT | InputTarget::Flags::DISPATCH_AS_IS);
wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
- state.addOrUpdateWindow(newWallpaper, wallpaperFlags, pointerIds, downTimeInTarget);
+ state.addOrUpdateWindow(newWallpaper, wallpaperFlags, deviceId, pointerIds,
+ downTimeInTarget);
std::shared_ptr<Connection> wallpaperConnection =
getConnectionLocked(newWallpaper->getToken());
if (wallpaperConnection != nullptr) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 20fe0ca..37f569e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -89,7 +89,7 @@
void dump(std::string& dump) override;
void monitor() override;
- bool waitForIdle() override;
+ bool waitForIdle() const override;
status_t start() override;
status_t stop() override;
@@ -104,7 +104,7 @@
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
android::os::InputEventInjectionResult injectInputEvent(
- const InputEvent* event, std::optional<int32_t> targetUid,
+ const InputEvent* event, std::optional<gui::Uid> targetUid,
android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
uint32_t policyFlags) override;
@@ -119,7 +119,7 @@
void setFocusedDisplay(int32_t displayId) override;
void setInputDispatchMode(bool enabled, bool frozen) override;
void setInputFilterEnabled(bool enabled) override;
- bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission,
+ bool setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid, bool hasPermission,
int32_t displayId) override;
void setMaximumObscuringOpacityForTouch(float opacity) override;
@@ -132,7 +132,7 @@
void setFocusedWindow(const android::gui::FocusRequest&) override;
base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
const std::string& name,
- int32_t pid) override;
+ gui::Pid pid) override;
status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
status_t pilferPointers(const sp<IBinder>& token) override;
void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
@@ -169,10 +169,10 @@
InputDispatcherPolicyInterface& mPolicy;
android::InputDispatcherConfiguration mConfig GUARDED_BY(mLock);
- std::mutex mLock;
+ mutable std::mutex mLock;
std::condition_variable mDispatcherIsAlive;
- std::condition_variable mDispatcherEnteredIdle;
+ mutable std::condition_variable mDispatcherEnteredIdle;
sp<Looper> mLooper;
@@ -271,7 +271,7 @@
mConnectionsByToken GUARDED_BY(mLock);
// Find a monitor pid by the provided token.
- std::optional<int32_t> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
+ std::optional<gui::Pid> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
// Input channels that will receive a copy of all input events sent to the provided display.
std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
@@ -522,10 +522,10 @@
void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock);
void sendWindowUnresponsiveCommandLocked(const sp<IBinder>& connectionToken,
- std::optional<int32_t> pid, std::string reason)
+ std::optional<gui::Pid> pid, std::string reason)
REQUIRES(mLock);
void sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken,
- std::optional<int32_t> pid) REQUIRES(mLock);
+ std::optional<gui::Pid> pid) REQUIRES(mLock);
// Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
// AnrTracker must be kept in-sync with all responsive connection.waitQueues.
@@ -574,7 +574,7 @@
bool hasBlockingOcclusion;
float obscuringOpacity;
std::string obscuringPackage;
- int32_t obscuringUid;
+ gui::Uid obscuringUid = gui::Uid::INVALID;
std::vector<std::string> debugInfo;
};
@@ -683,6 +683,7 @@
const std::string& reason) REQUIRES(mLock);
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
REQUIRES(mLock);
+ std::map<int32_t /*displayId*/, InputVerifier> mVerifiersByDisplay;
bool afterKeyEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
bool handled) REQUIRES(mLock);
@@ -702,22 +703,22 @@
void traceWaitQueueLength(const Connection& connection);
// Check window ownership
- bool focusedWindowIsOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
- bool recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
+ bool focusedWindowIsOwnedByLocked(gui::Pid pid, gui::Uid uid) REQUIRES(mLock);
+ bool recentWindowsAreOwnedByLocked(gui::Pid pid, gui::Uid uid) REQUIRES(mLock);
sp<InputReporterInterface> mReporter;
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t pointerId,
+ TouchState& state, int32_t deviceId, int32_t pointerId,
std::vector<InputTarget>& targets) const REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<android::gui::WindowInfoHandle> fromWindowHandle,
const sp<android::gui::WindowInfoHandle> toWindowHandle,
- TouchState& state, std::bitset<MAX_POINTER_ID + 1> pointerIds)
- REQUIRES(mLock);
+ TouchState& state, int32_t deviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds) REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
index 43a82d5..204791e 100644
--- a/services/inputflinger/dispatcher/Monitor.cpp
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -19,7 +19,7 @@
namespace android::inputdispatcher {
// --- Monitor ---
-Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid)
+Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, gui::Pid pid)
: inputChannel(inputChannel), pid(pid) {}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index 7b51191..1b1eb3a 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -16,6 +16,7 @@
#pragma once
+#include <gui/PidUid.h>
#include <input/InputTransport.h>
namespace android::inputdispatcher {
@@ -23,9 +24,9 @@
struct Monitor {
std::shared_ptr<InputChannel> inputChannel; // never null
- int32_t pid;
+ gui::Pid pid;
- explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid);
+ explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, gui::Pid pid);
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 0a61d48..ef188c7 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -31,18 +31,19 @@
*this = TouchState();
}
-void TouchState::removeTouchedPointer(int32_t pointerId) {
+void TouchState::removeTouchingPointer(int32_t removedDeviceId, int32_t pointerId) {
for (TouchedWindow& touchedWindow : windows) {
- touchedWindow.removeTouchingPointer(pointerId);
+ touchedWindow.removeTouchingPointer(removedDeviceId, pointerId);
}
clearWindowsWithoutPointers();
}
-void TouchState::removeTouchedPointerFromWindow(
- int32_t pointerId, const sp<android::gui::WindowInfoHandle>& windowHandle) {
+void TouchState::removeTouchingPointerFromWindow(
+ int32_t removedDeviceId, int32_t pointerId,
+ const sp<android::gui::WindowInfoHandle>& windowHandle) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.removeTouchingPointer(pointerId);
+ touchedWindow.removeTouchingPointer(removedDeviceId, pointerId);
clearWindowsWithoutPointers();
return;
}
@@ -58,13 +59,14 @@
void TouchState::clearWindowsWithoutPointers() {
std::erase_if(windows, [](const TouchedWindow& w) {
- return w.pointerIds.none() && !w.hasHoveringPointers();
+ return !w.hasTouchingPointers() && !w.hasHoveringPointers();
});
}
void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags,
- std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ int32_t addedDeviceId,
+ std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
std::optional<nsecs_t> firstDownTimeInTarget) {
for (TouchedWindow& touchedWindow : windows) {
// We do not compare windows by token here because two windows that share the same token
@@ -75,11 +77,11 @@
touchedWindow.targetFlags.clear(InputTarget::Flags::DISPATCH_AS_IS);
}
// For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
- // downTime set initially. Need to update existing window when an pointer is down for
- // the window.
- touchedWindow.pointerIds |= pointerIds;
- if (!touchedWindow.firstDownTimeInTarget.has_value()) {
- touchedWindow.firstDownTimeInTarget = firstDownTimeInTarget;
+ // downTime set initially. Need to update existing window when a pointer is down for the
+ // window.
+ touchedWindow.addTouchingPointers(addedDeviceId, touchingPointerIds);
+ if (firstDownTimeInTarget) {
+ touchedWindow.trySetDownTimeInTarget(addedDeviceId, *firstDownTimeInTarget);
}
return;
}
@@ -87,8 +89,10 @@
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
touchedWindow.targetFlags = targetFlags;
- touchedWindow.pointerIds = pointerIds;
- touchedWindow.firstDownTimeInTarget = firstDownTimeInTarget;
+ touchedWindow.addTouchingPointers(addedDeviceId, touchingPointerIds);
+ if (firstDownTimeInTarget) {
+ touchedWindow.trySetDownTimeInTarget(addedDeviceId, *firstDownTimeInTarget);
+ }
windows.push_back(touchedWindow);
}
@@ -130,12 +134,12 @@
}
}
-void TouchState::cancelPointersForWindowsExcept(std::bitset<MAX_POINTER_ID + 1> pointerIds,
+void TouchState::cancelPointersForWindowsExcept(int32_t touchedDeviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
const sp<IBinder>& token) {
- if (pointerIds.none()) return;
- std::for_each(windows.begin(), windows.end(), [&pointerIds, &token](TouchedWindow& w) {
+ std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
if (w.windowHandle->getToken() != token) {
- w.pointerIds &= ~pointerIds;
+ w.removeTouchingPointers(touchedDeviceId, pointerIds);
}
});
clearWindowsWithoutPointers();
@@ -149,24 +153,29 @@
*/
void TouchState::cancelPointersForNonPilferingWindows() {
// First, find all pointers that are being pilfered, across all windows
- std::bitset<MAX_POINTER_ID + 1> allPilferedPointerIds;
- std::for_each(windows.begin(), windows.end(), [&allPilferedPointerIds](const TouchedWindow& w) {
- allPilferedPointerIds |= w.pilferedPointerIds;
- });
+ std::map<int32_t /*deviceId*/, std::bitset<MAX_POINTER_ID + 1>> allPilferedPointerIdsByDevice;
+ for (const TouchedWindow& w : windows) {
+ for (const auto& [iterDeviceId, pilferedPointerIds] : w.getPilferingPointers()) {
+ allPilferedPointerIdsByDevice[iterDeviceId] |= pilferedPointerIds;
+ }
+ };
// Optimization: most of the time, pilfering does not occur
- if (allPilferedPointerIds.none()) return;
+ if (allPilferedPointerIdsByDevice.empty()) return;
// Now, remove all pointers from every window that's being pilfered by other windows.
// For example, if window A is pilfering pointer 1 (only), and window B is pilfering pointer 2
// (only), the remove pointer 2 from window A and pointer 1 from window B. Usually, the set of
// pilfered pointers will be disjoint across all windows, but there's no reason to cause that
// limitation here.
- std::for_each(windows.begin(), windows.end(), [&allPilferedPointerIds](TouchedWindow& w) {
- std::bitset<MAX_POINTER_ID + 1> pilferedByOtherWindows =
- w.pilferedPointerIds ^ allPilferedPointerIds;
- w.pointerIds &= ~pilferedByOtherWindows;
- });
+ for (const auto& [iterDeviceId, allPilferedPointerIds] : allPilferedPointerIdsByDevice) {
+ std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
+ std::bitset<MAX_POINTER_ID + 1> pilferedByOtherWindows =
+ w.getPilferingPointers(iterDeviceId) ^ allPilferedPointerIds;
+ // Remove all pointers pilfered by other windows
+ w.removeTouchingPointers(iterDeviceId, pilferedByOtherWindows);
+ });
+ }
clearWindowsWithoutPointers();
}
@@ -216,7 +225,7 @@
bool TouchState::isDown() const {
return std::any_of(windows.begin(), windows.end(),
- [](const TouchedWindow& window) { return window.pointerIds.any(); });
+ [](const TouchedWindow& window) { return window.hasTouchingPointers(); });
}
bool TouchState::hasHoveringPointers() const {
@@ -245,12 +254,9 @@
void TouchState::removeAllPointersForDevice(int32_t removedDeviceId) {
for (TouchedWindow& window : windows) {
window.removeAllHoveringPointersForDevice(removedDeviceId);
+ window.removeAllTouchingPointersForDevice(removedDeviceId);
}
- if (deviceId == removedDeviceId) {
- for (TouchedWindow& window : windows) {
- window.removeAllTouchingPointers();
- }
- }
+
clearWindowsWithoutPointers();
}
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 15b840f..a2a9f75 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -43,24 +43,25 @@
void reset();
void clearWindowsWithoutPointers();
- void removeTouchedPointer(int32_t pointerId);
- void removeTouchedPointerFromWindow(int32_t pointerId,
- const sp<android::gui::WindowInfoHandle>& windowHandle);
+ void removeTouchingPointer(int32_t deviceId, int32_t pointerId);
+ void removeTouchingPointerFromWindow(int32_t deviceId, int32_t pointerId,
+ const sp<android::gui::WindowInfoHandle>& windowHandle);
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- ftl::Flags<InputTarget::Flags> targetFlags,
- std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ ftl::Flags<InputTarget::Flags> targetFlags, int32_t deviceId,
+ std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
int32_t deviceId, int32_t hoveringPointerId);
void removeHoveringPointer(int32_t deviceId, int32_t hoveringPointerId);
void clearHoveringPointers();
- void removeAllPointersForDevice(int32_t removedDeviceId);
+ void removeAllPointersForDevice(int32_t deviceId);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
// Cancel pointers for current set of windows except the window with particular binder token.
- void cancelPointersForWindowsExcept(std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ void cancelPointersForWindowsExcept(int32_t deviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
const sp<IBinder>& token);
// Cancel pointers for current set of non-pilfering windows i.e. windows with isPilferingWindow
// set to false.
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index d55d657..6ab97f8 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -16,6 +16,7 @@
#include "TouchedWindow.h"
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <input/PrintTools.h>
@@ -26,67 +27,228 @@
namespace inputdispatcher {
bool TouchedWindow::hasHoveringPointers() const {
- return !mHoveringPointerIdsByDevice.empty();
+ for (const auto& [_, state] : mDeviceStates) {
+ if (state.hoveringPointerIds.any()) {
+ return true;
+ }
+ }
+ return false;
}
bool TouchedWindow::hasHoveringPointers(int32_t deviceId) const {
- return mHoveringPointerIdsByDevice.find(deviceId) != mHoveringPointerIdsByDevice.end();
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return false;
+ }
+ const DeviceState& state = stateIt->second;
+
+ return state.hoveringPointerIds.any();
}
void TouchedWindow::clearHoveringPointers() {
- mHoveringPointerIdsByDevice.clear();
+ for (auto& [_, state] : mDeviceStates) {
+ state.hoveringPointerIds.reset();
+ }
+
+ std::erase_if(mDeviceStates, [](const auto& pair) { return !pair.second.hasPointers(); });
}
bool TouchedWindow::hasHoveringPointer(int32_t deviceId, int32_t pointerId) const {
- auto it = mHoveringPointerIdsByDevice.find(deviceId);
- if (it == mHoveringPointerIdsByDevice.end()) {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
return false;
}
- return it->second.test(pointerId);
+ const DeviceState& state = stateIt->second;
+
+ return state.hoveringPointerIds.test(pointerId);
}
void TouchedWindow::addHoveringPointer(int32_t deviceId, int32_t pointerId) {
- const auto [it, _] = mHoveringPointerIdsByDevice.insert({deviceId, {}});
- it->second.set(pointerId);
+ mDeviceStates[deviceId].hoveringPointerIds.set(pointerId);
}
-void TouchedWindow::removeTouchingPointer(int32_t pointerId) {
- pointerIds.reset(pointerId);
- pilferedPointerIds.reset(pointerId);
- if (pointerIds.none()) {
- firstDownTimeInTarget.reset();
+void TouchedWindow::addTouchingPointer(int32_t deviceId, int32_t pointerId) {
+ mDeviceStates[deviceId].touchingPointerIds.set(pointerId);
+}
+
+void TouchedWindow::addTouchingPointers(int32_t deviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointers) {
+ mDeviceStates[deviceId].touchingPointerIds |= pointers;
+}
+
+bool TouchedWindow::hasTouchingPointers() const {
+ for (const auto& [_, state] : mDeviceStates) {
+ if (state.touchingPointerIds.any()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TouchedWindow::hasTouchingPointers(int32_t deviceId) const {
+ return getTouchingPointers(deviceId).any();
+}
+
+bool TouchedWindow::hasTouchingPointer(int32_t deviceId, int32_t pointerId) const {
+ return getTouchingPointers(deviceId).test(pointerId);
+}
+
+std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getTouchingPointers(int32_t deviceId) const {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return {};
+ }
+ const DeviceState& state = stateIt->second;
+
+ return state.touchingPointerIds;
+}
+
+void TouchedWindow::removeTouchingPointer(int32_t deviceId, int32_t pointerId) {
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ pointerIds.set(pointerId, true);
+
+ removeTouchingPointers(deviceId, pointerIds);
+}
+
+void TouchedWindow::removeTouchingPointers(int32_t deviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointers) {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return;
+ }
+ DeviceState& state = stateIt->second;
+
+ state.touchingPointerIds &= ~pointers;
+ state.pilferingPointerIds &= ~pointers;
+
+ if (!state.hasPointers()) {
+ mDeviceStates.erase(stateIt);
}
}
-void TouchedWindow::removeAllTouchingPointers() {
- pointerIds.reset();
+std::set<int32_t> TouchedWindow::getTouchingDeviceIds() const {
+ std::set<int32_t> deviceIds;
+ for (const auto& [deviceId, _] : mDeviceStates) {
+ deviceIds.insert(deviceId);
+ }
+ return deviceIds;
+}
+
+bool TouchedWindow::hasPilferingPointers(int32_t deviceId) const {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return false;
+ }
+ const DeviceState& state = stateIt->second;
+
+ return state.pilferingPointerIds.any();
+}
+
+void TouchedWindow::addPilferingPointers(int32_t deviceId,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds) {
+ mDeviceStates[deviceId].pilferingPointerIds |= pointerIds;
+}
+
+void TouchedWindow::addPilferingPointer(int32_t deviceId, int32_t pointerId) {
+ mDeviceStates[deviceId].pilferingPointerIds.set(pointerId);
+}
+
+std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getPilferingPointers(int32_t deviceId) const {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return {};
+ }
+ const DeviceState& state = stateIt->second;
+
+ return state.pilferingPointerIds;
+}
+
+std::map<int32_t, std::bitset<MAX_POINTER_ID + 1>> TouchedWindow::getPilferingPointers() const {
+ std::map<int32_t, std::bitset<MAX_POINTER_ID + 1>> out;
+ for (const auto& [deviceId, state] : mDeviceStates) {
+ out.emplace(deviceId, state.pilferingPointerIds);
+ }
+ return out;
+}
+
+std::optional<nsecs_t> TouchedWindow::getDownTimeInTarget(int32_t deviceId) const {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return {};
+ }
+ const DeviceState& state = stateIt->second;
+ return state.downTimeInTarget;
+}
+
+void TouchedWindow::trySetDownTimeInTarget(int32_t deviceId, nsecs_t downTime) {
+ auto [stateIt, _] = mDeviceStates.try_emplace(deviceId);
+ DeviceState& state = stateIt->second;
+
+ if (!state.downTimeInTarget) {
+ state.downTimeInTarget = downTime;
+ }
+}
+
+void TouchedWindow::removeAllTouchingPointersForDevice(int32_t deviceId) {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return;
+ }
+ DeviceState& state = stateIt->second;
+
+ state.touchingPointerIds.reset();
+ state.pilferingPointerIds.reset();
+ state.downTimeInTarget.reset();
+
+ if (!state.hasPointers()) {
+ mDeviceStates.erase(stateIt);
+ }
}
void TouchedWindow::removeHoveringPointer(int32_t deviceId, int32_t pointerId) {
- const auto it = mHoveringPointerIdsByDevice.find(deviceId);
- if (it == mHoveringPointerIdsByDevice.end()) {
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
return;
}
- it->second.set(pointerId, false);
+ DeviceState& state = stateIt->second;
- if (it->second.none()) {
- mHoveringPointerIdsByDevice.erase(deviceId);
+ state.hoveringPointerIds.set(pointerId, false);
+
+ if (!state.hasPointers()) {
+ mDeviceStates.erase(stateIt);
}
}
void TouchedWindow::removeAllHoveringPointersForDevice(int32_t deviceId) {
- mHoveringPointerIdsByDevice.erase(deviceId);
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return;
+ }
+ DeviceState& state = stateIt->second;
+
+ state.hoveringPointerIds.reset();
+
+ if (!state.hasPointers()) {
+ mDeviceStates.erase(stateIt);
+ }
+}
+
+std::string TouchedWindow::deviceStateToString(const TouchedWindow::DeviceState& state) {
+ return StringPrintf("[touchingPointerIds=%s, "
+ "downTimeInTarget=%s, hoveringPointerIds=%s, pilferingPointerIds=%s]",
+ bitsetToString(state.touchingPointerIds).c_str(),
+ toString(state.downTimeInTarget).c_str(),
+ bitsetToString(state.hoveringPointerIds).c_str(),
+ bitsetToString(state.pilferingPointerIds).c_str());
}
std::string TouchedWindow::dump() const {
std::string out;
- std::string hoveringPointers =
- dumpMap(mHoveringPointerIdsByDevice, constToString, bitsetToString);
- out += StringPrintf("name='%s', pointerIds=%s, targetFlags=%s, firstDownTimeInTarget=%s, "
- "mHoveringPointerIdsByDevice=%s, pilferedPointerIds=%s\n",
- windowHandle->getName().c_str(), bitsetToString(pointerIds).c_str(),
- targetFlags.string().c_str(), toString(firstDownTimeInTarget).c_str(),
- hoveringPointers.c_str(), bitsetToString(pilferedPointerIds).c_str());
+ std::string deviceStates =
+ dumpMap(mDeviceStates, constToString, TouchedWindow::deviceStateToString);
+ out += StringPrintf("name='%s', targetFlags=%s, mDeviceStates=%s\n",
+ windowHandle->getName().c_str(), targetFlags.string().c_str(),
+ deviceStates.c_str());
return out;
}
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 43e7169..e50ede5 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -20,6 +20,7 @@
#include <input/Input.h>
#include <utils/BitSet.h>
#include <bitset>
+#include <set>
#include "InputTarget.h"
namespace android {
@@ -30,28 +31,62 @@
struct TouchedWindow {
sp<gui::WindowInfoHandle> windowHandle;
ftl::Flags<InputTarget::Flags> targetFlags;
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
- // The pointer ids of the pointers that this window is currently pilfering
- std::bitset<MAX_POINTER_ID + 1> pilferedPointerIds;
- // Time at which the first action down occurred on this window.
- // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE scenario.
- std::optional<nsecs_t> firstDownTimeInTarget;
+ // Hovering
bool hasHoveringPointers() const;
bool hasHoveringPointers(int32_t deviceId) const;
-
bool hasHoveringPointer(int32_t deviceId, int32_t pointerId) const;
void addHoveringPointer(int32_t deviceId, int32_t pointerId);
void removeHoveringPointer(int32_t deviceId, int32_t pointerId);
- void removeTouchingPointer(int32_t pointerId);
- void removeAllTouchingPointers();
+ // Touching
+ bool hasTouchingPointer(int32_t deviceId, int32_t pointerId) const;
+ bool hasTouchingPointers() const;
+ bool hasTouchingPointers(int32_t deviceId) const;
+ std::bitset<MAX_POINTER_ID + 1> getTouchingPointers(int32_t deviceId) const;
+ void addTouchingPointer(int32_t deviceId, int32_t pointerId);
+ void addTouchingPointers(int32_t deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
+ void removeTouchingPointer(int32_t deviceId, int32_t pointerId);
+ void removeTouchingPointers(int32_t deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
+ /**
+ * Get the currently active touching device id. If there isn't exactly 1 touching device, return
+ * nullopt.
+ */
+ std::set<int32_t> getTouchingDeviceIds() const;
+
+ // Pilfering pointers
+ bool hasPilferingPointers(int32_t deviceId) const;
+ void addPilferingPointers(int32_t deviceId, std::bitset<MAX_POINTER_ID + 1> pointerIds);
+ void addPilferingPointer(int32_t deviceId, int32_t pointerId);
+ std::bitset<MAX_POINTER_ID + 1> getPilferingPointers(int32_t deviceId) const;
+ std::map<int32_t, std::bitset<MAX_POINTER_ID + 1>> getPilferingPointers() const;
+
+ // Down time
+ std::optional<nsecs_t> getDownTimeInTarget(int32_t deviceId) const;
+ void trySetDownTimeInTarget(int32_t deviceId, nsecs_t downTime);
+
+ void removeAllTouchingPointersForDevice(int32_t deviceId);
void removeAllHoveringPointersForDevice(int32_t deviceId);
void clearHoveringPointers();
std::string dump() const;
private:
- std::map<int32_t /*deviceId*/, std::bitset<MAX_POINTER_ID + 1>> mHoveringPointerIdsByDevice;
+ struct DeviceState {
+ std::bitset<MAX_POINTER_ID + 1> touchingPointerIds;
+ // The pointer ids of the pointers that this window is currently pilfering, by device
+ std::bitset<MAX_POINTER_ID + 1> pilferingPointerIds;
+ // Time at which the first action down occurred on this window, for each device
+ // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE
+ // scenario.
+ std::optional<nsecs_t> downTimeInTarget;
+ std::bitset<MAX_POINTER_ID + 1> hoveringPointerIds;
+
+ bool hasPointers() const { return touchingPointerIds.any() || hoveringPointerIds.any(); };
+ };
+
+ std::map<int32_t /*deviceId*/, DeviceState> mDeviceStates;
+
+ static std::string deviceStateToString(const TouchedWindow::DeviceState& state);
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index c752ddd..49597e2 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -50,7 +50,7 @@
* Return true if the dispatcher is idle.
* Return false if the timeout waiting for the dispatcher to become idle has expired.
*/
- virtual bool waitForIdle() = 0;
+ virtual bool waitForIdle() const = 0;
/* Make the dispatcher start processing events.
*
@@ -76,7 +76,7 @@
* perform all necessary permission checks prior to injecting events.
*/
virtual android::os::InputEventInjectionResult injectInputEvent(
- const InputEvent* event, std::optional<int32_t> targetUid,
+ const InputEvent* event, std::optional<gui::Uid> targetUid,
android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
uint32_t policyFlags) = 0;
@@ -134,7 +134,7 @@
*
* Returns true when changing touch mode state.
*/
- virtual bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission,
+ virtual bool setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid, bool hasPermission,
int32_t displayId) = 0;
/**
@@ -182,7 +182,7 @@
*/
virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
const std::string& name,
- int32_t pid) = 0;
+ gui::Pid pid) = 0;
/* Removes input channels that will no longer receive input events.
*
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 69caa99..d50f74d 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -54,7 +54,7 @@
* pid of the owner. The string reason contains information about the input event that we
* haven't received a response for.
*/
- virtual void notifyWindowUnresponsive(const sp<IBinder>& token, std::optional<int32_t> pid,
+ virtual void notifyWindowUnresponsive(const sp<IBinder>& token, std::optional<gui::Pid> pid,
const std::string& reason) = 0;
/* Notifies the system that a window just became responsive. This is only called after the
@@ -62,7 +62,7 @@
* no longer should be shown to the user. The window is eligible to cause a new ANR in the
* future.
*/
- virtual void notifyWindowResponsive(const sp<IBinder>& token, std::optional<int32_t> pid) = 0;
+ virtual void notifyWindowResponsive(const sp<IBinder>& token, std::optional<gui::Pid> pid) = 0;
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
@@ -140,7 +140,7 @@
/* Notifies the policy that there was an input device interaction with apps. */
virtual void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
- const std::set<int32_t>& uids) = 0;
+ const std::set<gui::Uid>& uids) = 0;
};
} // namespace android
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index 7d29dd9..736b1e0 100644
--- a/services/inputflinger/include/NotifyArgs.h
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -104,9 +104,9 @@
MotionClassification classification;
int32_t edgeFlags;
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
+ // Vectors 'pointerProperties' and 'pointerCoords' must always have the same number of elements
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
float xPrecision;
float yPrecision;
/**
@@ -131,11 +131,13 @@
float yCursorPosition, nsecs_t downTime,
const std::vector<TouchVideoFrame>& videoFrames);
- NotifyMotionArgs(const NotifyMotionArgs& other);
+ NotifyMotionArgs(const NotifyMotionArgs& other) = default;
NotifyMotionArgs& operator=(const android::NotifyMotionArgs&) = default;
bool operator==(const NotifyMotionArgs& rhs) const;
+ inline size_t getPointerCount() const { return pointerProperties.size(); }
+
std::string dump() const;
};
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index b0edb57..ccb8773 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -98,12 +98,14 @@
android: {
shared_libs: [
"libinput",
+ "libstatspull",
],
},
host: {
static_libs: [
"libinput",
"libbinder",
+ "libstatspull",
],
},
},
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 04747cc..4d0e13e 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -864,6 +864,30 @@
strerror(errno));
}
+void EventHub::populateDeviceAbsoluteAxisInfo(Device& device) {
+ for (int axis = 0; axis <= ABS_MAX; axis++) {
+ if (!device.absBitmask.test(axis)) {
+ continue;
+ }
+ struct input_absinfo info {};
+ if (ioctl(device.fd, EVIOCGABS(axis), &info)) {
+ ALOGE("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
+ device.identifier.name.c_str(), device.fd, errno);
+ continue;
+ }
+ if (info.minimum == info.maximum) {
+ continue;
+ }
+ RawAbsoluteAxisInfo& outAxisInfo = device.rawAbsoluteAxisInfoCache[axis];
+ outAxisInfo.valid = true;
+ outAxisInfo.minValue = info.minimum;
+ outAxisInfo.maxValue = info.maximum;
+ outAxisInfo.flat = info.flat;
+ outAxisInfo.fuzz = info.fuzz;
+ outAxisInfo.resolution = info.resolution;
+ }
+}
+
InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
@@ -894,31 +918,20 @@
status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
RawAbsoluteAxisInfo* outAxisInfo) const {
outAxisInfo->clear();
-
- if (axis >= 0 && axis <= ABS_MAX) {
- std::scoped_lock _l(mLock);
-
- Device* device = getDeviceLocked(deviceId);
- if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
- struct input_absinfo info;
- if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
- ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
- device->identifier.name.c_str(), device->fd, errno);
- return -errno;
- }
-
- if (info.minimum != info.maximum) {
- outAxisInfo->valid = true;
- outAxisInfo->minValue = info.minimum;
- outAxisInfo->maxValue = info.maximum;
- outAxisInfo->flat = info.flat;
- outAxisInfo->fuzz = info.fuzz;
- outAxisInfo->resolution = info.resolution;
- }
- return OK;
- }
+ if (axis < 0 || axis > ABS_MAX) {
+ return -1;
}
- return -1;
+ std::scoped_lock _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr) {
+ return -1;
+ }
+ auto it = device->rawAbsoluteAxisInfoCache.find(axis);
+ if (it == device->rawAbsoluteAxisInfoCache.end()) {
+ return -1;
+ }
+ *outAxisInfo = it->second;
+ return OK;
}
bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
@@ -2435,6 +2448,9 @@
device->configureFd();
+ // read absolute axis info for all available axes for the device
+ populateDeviceAbsoluteAxisInfo(*device);
+
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=%s, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
deviceId, fd, devicePath.c_str(), device->identifier.name.c_str(),
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 20612c7..024187f 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -615,6 +615,7 @@
std::unique_ptr<PropertyMap> configuration;
std::unique_ptr<VirtualKeyMap> virtualKeyMap;
KeyMap keyMap;
+ std::unordered_map<int /*axis*/, RawAbsoluteAxisInfo> rawAbsoluteAxisInfoCache;
bool ffEffectPlaying;
int16_t ffEffectId; // initially -1
@@ -717,6 +718,13 @@
void addDeviceInputInotify();
void addDeviceInotify();
+ /**
+ * AbsoluteAxisInfo remains unchanged for the lifetime of the device, hence
+ * we can read and store it with device
+ * @param device target device
+ */
+ static void populateDeviceAbsoluteAxisInfo(Device& device);
+
// Protect all internal state.
mutable std::mutex mLock;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index f4d50b8..f2b0a4b 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1889,9 +1889,9 @@
uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
// Skip checking whether the pointer is inside the physical frame if the device is in
- // unscaled mode.
+ // unscaled or pointer mode.
if (!isPointInsidePhysicalFrame(pointer.x, pointer.y) &&
- mDeviceMode != DeviceMode::UNSCALED) {
+ mDeviceMode != DeviceMode::UNSCALED && mDeviceMode != DeviceMode::POINTER) {
// If exactly one pointer went down, check for virtual key hit.
// Otherwise, we will drop the entire stroke.
if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index c72425a..ca4dd1e 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -16,8 +16,11 @@
#include "../Macros.h"
+#include <algorithm>
#include <chrono>
+#include <iterator>
#include <limits>
+#include <map>
#include <optional>
#include <android-base/stringprintf.h>
@@ -26,6 +29,8 @@
#include <input/PrintTools.h>
#include <linux/input-event-codes.h>
#include <log/log_main.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
#include "TouchCursorInputMapperCommon.h"
#include "TouchpadInputMapper.h"
#include "ui/Rotation.h"
@@ -169,6 +174,106 @@
mapper->consumeGesture(gesture);
}
+int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus) {
+ // When adding cases to this switch, also add them to the copy of this method in
+ // InputDeviceMetricsCollector.cpp.
+ // TODO(b/286394420): deduplicate this method with the one in InputDeviceMetricsCollector.cpp.
+ switch (linuxBus) {
+ case BUS_USB:
+ return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USB;
+ case BUS_BLUETOOTH:
+ return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__BLUETOOTH;
+ default:
+ return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__OTHER;
+ }
+}
+
+class MetricsAccumulator {
+public:
+ static MetricsAccumulator& getInstance() {
+ static MetricsAccumulator sAccumulator;
+ return sAccumulator;
+ }
+
+ void recordFinger(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].fingers++; }
+
+ void recordPalm(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].palms++; }
+
+ // Checks whether a Gesture struct is for the end of a gesture that we log metrics for, and
+ // records it if so.
+ void processGesture(const TouchpadInputMapper::MetricsIdentifier& id, const Gesture& gesture) {
+ switch (gesture.type) {
+ case kGestureTypeFling:
+ if (gesture.details.fling.fling_state == GESTURES_FLING_START) {
+ // Indicates the end of a two-finger scroll gesture.
+ mCounters[id].twoFingerSwipeGestures++;
+ }
+ break;
+ case kGestureTypeSwipeLift:
+ mCounters[id].threeFingerSwipeGestures++;
+ break;
+ case kGestureTypeFourFingerSwipeLift:
+ mCounters[id].fourFingerSwipeGestures++;
+ break;
+ case kGestureTypePinch:
+ if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) {
+ mCounters[id].pinchGestures++;
+ }
+ break;
+ default:
+ // We're not interested in any other gestures.
+ break;
+ }
+ }
+
+private:
+ MetricsAccumulator() {
+ AStatsManager_setPullAtomCallback(android::util::TOUCHPAD_USAGE, /*metadata=*/nullptr,
+ MetricsAccumulator::pullAtomCallback, /*cookie=*/nullptr);
+ }
+
+ ~MetricsAccumulator() { AStatsManager_clearPullAtomCallback(android::util::TOUCHPAD_USAGE); }
+
+ static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag,
+ AStatsEventList* outEventList,
+ void* cookie) {
+ LOG_ALWAYS_FATAL_IF(atomTag != android::util::TOUCHPAD_USAGE);
+ MetricsAccumulator& accumulator = MetricsAccumulator::getInstance();
+ accumulator.produceAtoms(outEventList);
+ accumulator.resetCounters();
+ return AStatsManager_PULL_SUCCESS;
+ }
+
+ void produceAtoms(AStatsEventList* outEventList) const {
+ for (auto& [id, counters] : mCounters) {
+ auto [busId, vendorId, productId, versionId] = id;
+ addAStatsEvent(outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId,
+ versionId, linuxBusToInputDeviceBusEnum(busId), counters.fingers,
+ counters.palms, counters.twoFingerSwipeGestures,
+ counters.threeFingerSwipeGestures, counters.fourFingerSwipeGestures,
+ counters.pinchGestures);
+ }
+ }
+
+ void resetCounters() { mCounters.clear(); }
+
+ // Stores the counters for a specific touchpad model. Fields have the same meanings as those of
+ // the TouchpadUsage atom; see that definition for detailed documentation.
+ struct Counters {
+ int32_t fingers = 0;
+ int32_t palms = 0;
+
+ int32_t twoFingerSwipeGestures = 0;
+ int32_t threeFingerSwipeGestures = 0;
+ int32_t fourFingerSwipeGestures = 0;
+ int32_t pinchGestures = 0;
+ };
+
+ // Metrics are aggregated by device model and version, so if two devices of the same model and
+ // version are connected at once, they will have the same counters.
+ std::map<TouchpadInputMapper::MetricsIdentifier, Counters> mCounters;
+};
+
} // namespace
TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext,
@@ -178,7 +283,8 @@
mPointerController(getContext()->getPointerController(getDeviceId())),
mStateConverter(deviceContext, mMotionAccumulator),
mGestureConverter(*getContext(), deviceContext, getDeviceId()),
- mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()) {
+ mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()),
+ mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) {
RawAbsoluteAxisInfo slotAxisInfo;
deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
@@ -331,12 +437,39 @@
}
std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
if (state) {
+ updatePalmDetectionMetrics();
return sendHardwareState(rawEvent->when, rawEvent->readTime, *state);
} else {
return {};
}
}
+void TouchpadInputMapper::updatePalmDetectionMetrics() {
+ std::set<int32_t> currentTrackingIds;
+ for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
+ const MultiTouchMotionAccumulator::Slot& slot = mMotionAccumulator.getSlot(i);
+ if (!slot.isInUse()) {
+ continue;
+ }
+ currentTrackingIds.insert(slot.getTrackingId());
+ if (slot.getToolType() == ToolType::PALM) {
+ mPalmTrackingIds.insert(slot.getTrackingId());
+ }
+ }
+ std::vector<int32_t> liftedTouches;
+ std::set_difference(mLastFrameTrackingIds.begin(), mLastFrameTrackingIds.end(),
+ currentTrackingIds.begin(), currentTrackingIds.end(),
+ std::inserter(liftedTouches, liftedTouches.begin()));
+ for (int32_t trackingId : liftedTouches) {
+ if (mPalmTrackingIds.erase(trackingId) > 0) {
+ MetricsAccumulator::getInstance().recordPalm(mMetricsId);
+ } else {
+ MetricsAccumulator::getInstance().recordFinger(mMetricsId);
+ }
+ }
+ mLastFrameTrackingIds = currentTrackingIds;
+}
+
std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs) {
ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "New hardware state: %s", schs.state.String().c_str());
@@ -363,8 +496,10 @@
std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out = {};
+ MetricsAccumulator& metricsAccumulator = MetricsAccumulator::getInstance();
for (Gesture& gesture : mGesturesToProcess) {
out += mGestureConverter.handleGesture(when, readTime, gesture);
+ metricsAccumulator.processGesture(mMetricsId, gesture);
}
mGesturesToProcess.clear();
return out;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 23d0fd3..73ca5af 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -18,6 +18,7 @@
#include <list>
#include <memory>
+#include <set>
#include <vector>
#include <PointerControllerInterface.h>
@@ -58,10 +59,16 @@
void consumeGesture(const Gesture* gesture);
+ // A subset of InputDeviceIdentifier used for logging metrics, to avoid storing a copy of the
+ // strings in that bigger struct.
+ using MetricsIdentifier = std::tuple<uint16_t /*busId*/, uint16_t /*vendorId*/,
+ uint16_t /*productId*/, uint16_t /*version*/>;
+
private:
void resetGestureInterpreter(nsecs_t when);
explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
+ void updatePalmDetectionMetrics();
[[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs);
[[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
@@ -86,6 +93,15 @@
bool mProcessing = false;
bool mResettingInterpreter = false;
std::vector<Gesture> mGesturesToProcess;
+
+ static MetricsIdentifier metricsIdFromInputDeviceIdentifier(const InputDeviceIdentifier& id) {
+ return std::make_tuple(id.bus, id.vendor, id.product, id.version);
+ }
+ const MetricsIdentifier mMetricsId;
+ // Tracking IDs for touches on the pad in the last evdev frame.
+ std::set<int32_t> mLastFrameTrackingIds;
+ // Tracking IDs for touches that have at some point been reported as palms by the touchpad.
+ std::set<int32_t> mPalmTrackingIds;
};
} // namespace android
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index c555d95..0aa5e23 100644
--- a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
+++ b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
@@ -18,11 +18,12 @@
#include <gtest/gtest.h>
#include <gui/constants.h>
+#include <input/EventBuilders.h>
#include <linux/input.h>
+
#include <array>
#include <tuple>
-#include "EventBuilders.h"
#include "TestInputListener.h"
namespace android {
@@ -80,6 +81,14 @@
const InputDeviceInfo NON_ALPHABETIC_KEYBOARD_INFO =
generateTestDeviceInfo(DEVICE_ID, KEY_SOURCES, /*isAlphabetic=*/false);
+std::set<gui::Uid> uids(std::initializer_list<int32_t> vals) {
+ std::set<gui::Uid> set;
+ for (const auto val : vals) {
+ set.emplace(val);
+ }
+ return set;
+}
+
} // namespace
// --- InputDeviceMetricsCollectorDeviceClassificationTest ---
@@ -632,7 +641,7 @@
mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
// Notify interaction with UIDs before the device is used.
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1}));
// Use the device.
setCurrentTime(TIME + 100ns);
@@ -641,12 +650,12 @@
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
// Notify interaction for the wrong device.
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), /*uids=*/{42});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), uids({42}));
// Notify interaction after usage session would have expired.
// This interaction should not be tracked.
setCurrentTime(TIME + 200ns + USAGE_TIMEOUT);
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{2, 3});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({2, 3}));
// Use the device again, by starting a new usage session.
setCurrentTime(TIME + 300ns + USAGE_TIMEOUT);
@@ -665,14 +674,14 @@
UidUsageBreakdown expectedUidBreakdown;
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1}));
setCurrentTime(TIME + 100ns);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2}));
setCurrentTime(TIME + 200ns);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2, 3});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2, 3}));
expectedUidBreakdown.emplace_back(1, 200ns);
expectedUidBreakdown.emplace_back(2, 100ns);
@@ -691,39 +700,39 @@
UidUsageBreakdown expectedUidBreakdown;
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2}));
setCurrentTime(TIME + 100ns);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2}));
setCurrentTime(TIME + 200ns);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1}));
setCurrentTime(TIME + 300ns);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 3});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 3}));
setCurrentTime(TIME + 400ns);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 3});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 3}));
setCurrentTime(TIME + 200ns + USAGE_TIMEOUT);
expectedUidBreakdown.emplace_back(2, 100ns);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{4});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({4}));
setCurrentTime(TIME + 300ns + USAGE_TIMEOUT);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 4});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 4}));
setCurrentTime(TIME + 400ns + USAGE_TIMEOUT);
expectedUidBreakdown.emplace_back(3, 100ns);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{2, 3});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({2, 3}));
setCurrentTime(TIME + 500ns + USAGE_TIMEOUT);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{3});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({3}));
// Remove the device to force the usage session to be logged.
mMetricsCollector.notifyInputDevicesChanged({});
@@ -744,17 +753,17 @@
UidUsageBreakdown expectedUidBreakdown2;
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2}));
setCurrentTime(TIME + 100ns);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), /*uids=*/{1, 3});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), uids({1, 3}));
setCurrentTime(TIME + 200ns);
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2}));
mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2));
- mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), /*uids=*/{1, 3});
+ mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), uids({1, 3}));
setCurrentTime(TIME + 200ns + USAGE_TIMEOUT);
expectedUidBreakdown1.emplace_back(1, 200ns);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 8b74b25..5af9999 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,7 +16,6 @@
#include "../dispatcher/InputDispatcher.h"
#include "../BlockingQueue.h"
-#include "EventBuilders.h"
#include <android-base/properties.h>
#include <android-base/silent_death_test.h>
@@ -26,6 +25,7 @@
#include <fcntl.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <input/EventBuilders.h>
#include <input/Input.h>
#include <linux/input.h>
#include <sys/epoll.h>
@@ -95,15 +95,15 @@
AMOTION_EVENT_ACTION_POINTER_UP | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
// The default pid and uid for windows created on the primary display by the test.
-static constexpr int32_t WINDOW_PID = 999;
-static constexpr int32_t WINDOW_UID = 1001;
+static constexpr gui::Pid WINDOW_PID{999};
+static constexpr gui::Uid WINDOW_UID{1001};
// The default pid and uid for the windows created on the secondary display by the test.
-static constexpr int32_t SECONDARY_WINDOW_PID = 1010;
-static constexpr int32_t SECONDARY_WINDOW_UID = 1012;
+static constexpr gui::Pid SECONDARY_WINDOW_PID{1010};
+static constexpr gui::Uid SECONDARY_WINDOW_UID{1012};
// An arbitrary pid of the gesture monitor window
-static constexpr int32_t MONITOR_PID = 2001;
+static constexpr gui::Pid MONITOR_PID{2001};
static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
@@ -208,7 +208,10 @@
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
InputDispatcherConfiguration mConfig;
- using AnrResult = std::pair<sp<IBinder>, int32_t /*pid*/>;
+ struct AnrResult {
+ sp<IBinder> token{};
+ gui::Pid pid{-1};
+ };
public:
FakeInputDispatcherPolicy() = default;
@@ -298,15 +301,14 @@
void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
const sp<IBinder>& expectedToken,
- int32_t expectedPid) {
+ gui::Pid expectedPid) {
std::unique_lock lock(mLock);
android::base::ScopedLockAssertion assumeLocked(mLock);
AnrResult result;
ASSERT_NO_FATAL_FAILURE(result =
getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock));
- const auto& [token, pid] = result;
- ASSERT_EQ(expectedToken, token);
- ASSERT_EQ(expectedPid, pid);
+ ASSERT_EQ(expectedToken, result.token);
+ ASSERT_EQ(expectedPid, result.pid);
}
/** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
@@ -319,15 +321,14 @@
}
void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken,
- int32_t expectedPid) {
+ gui::Pid expectedPid) {
std::unique_lock lock(mLock);
android::base::ScopedLockAssertion assumeLocked(mLock);
AnrResult result;
ASSERT_NO_FATAL_FAILURE(
result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock));
- const auto& [token, pid] = result;
- ASSERT_EQ(expectedToken, token);
- ASSERT_EQ(expectedPid, pid);
+ ASSERT_EQ(expectedToken, result.token);
+ ASSERT_EQ(expectedPid, result.pid);
}
/** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
@@ -383,7 +384,9 @@
mPointerCaptureRequest.reset();
}
- void assertDropTargetEquals(const sp<IBinder>& targetToken) {
+ void assertDropTargetEquals(const InputDispatcherInterface& dispatcher,
+ const sp<IBinder>& targetToken) {
+ dispatcher.waitForIdle();
std::scoped_lock lock(mLock);
ASSERT_TRUE(mNotifyDropWindowWasCalled);
ASSERT_EQ(targetToken, mDropTargetWindowToken);
@@ -417,7 +420,7 @@
ASSERT_FALSE(mPokedUserActivity) << "Expected user activity not to have been poked";
}
- void assertNotifyDeviceInteractionWasCalled(int32_t deviceId, std::set<int32_t> uids) {
+ void assertNotifyDeviceInteractionWasCalled(int32_t deviceId, std::set<gui::Uid> uids) {
ASSERT_EQ(std::make_pair(deviceId, uids), mNotifiedInteractions.popWithTimeout(100ms));
}
@@ -450,7 +453,7 @@
std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
- BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<int32_t /*uid*/>>> mNotifiedInteractions;
+ BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
// All three ANR-related callbacks behave the same way, so we use this generic function to wait
// for a specific container to become non-empty. When the container is non-empty, return the
@@ -507,7 +510,7 @@
mConfigurationChangedTime = when;
}
- void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<int32_t> pid,
+ void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
const std::string&) override {
std::scoped_lock lock(mLock);
ASSERT_TRUE(pid.has_value());
@@ -516,7 +519,7 @@
}
void notifyWindowResponsive(const sp<IBinder>& connectionToken,
- std::optional<int32_t> pid) override {
+ std::optional<gui::Pid> pid) override {
std::scoped_lock lock(mLock);
ASSERT_TRUE(pid.has_value());
mResponsiveWindows.push({connectionToken, *pid});
@@ -624,7 +627,7 @@
}
void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
- const std::set<int32_t>& uids) override {
+ const std::set<gui::Uid>& uids) override {
ASSERT_TRUE(mNotifiedInteractions.emplace(deviceId, uids));
}
@@ -1443,12 +1446,12 @@
const std::string& getName() { return mName; }
- void setOwnerInfo(int32_t ownerPid, int32_t ownerUid) {
+ void setOwnerInfo(gui::Pid ownerPid, gui::Uid ownerUid) {
mInfo.ownerPid = ownerPid;
mInfo.ownerUid = ownerUid;
}
- int32_t getPid() const { return mInfo.ownerPid; }
+ gui::Pid getPid() const { return mInfo.ownerPid; }
void destroyReceiver() { mInputReceiver = nullptr; }
@@ -1469,7 +1472,7 @@
int32_t displayId = ADISPLAY_ID_NONE,
InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
- bool allowKeyRepeat = true, std::optional<int32_t> targetUid = {},
+ bool allowKeyRepeat = true, std::optional<gui::Uid> targetUid = {},
uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
KeyEvent event;
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -1510,7 +1513,7 @@
const std::unique_ptr<InputDispatcher>& dispatcher, const MotionEvent& event,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
- std::optional<int32_t> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
+ std::optional<gui::Uid> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
return dispatcher->injectInputEvent(&event, targetUid, injectionMode, injectionTimeout,
policyFlags);
}
@@ -1523,7 +1526,7 @@
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC),
- std::optional<int32_t> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
+ std::optional<gui::Uid> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
MotionEventBuilder motionBuilder =
MotionEventBuilder(action, source)
.displayId(displayId)
@@ -3644,6 +3647,9 @@
std::chrono::nanoseconds(interceptKeyTimeout).count());
}
+/**
+ * Keys with ACTION_UP are delivered immediately, even if a long 'intercept key timeout' is set.
+ */
TEST_F(InputDispatcherTest, InterceptKeyIfKeyUp) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
@@ -3655,12 +3661,14 @@
window->consumeFocusEvent(true);
- mFakePolicy->setInterceptKeyTimeout(150ms);
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
-
- // Window should receive key event immediately when same key up.
window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+ // Set a value that's significantly larger than the default consumption timeout. If the
+ // implementation is correct, the actual value doesn't matter; it won't slow down the test.
+ mFakePolicy->setInterceptKeyTimeout(600ms);
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
+ // Window should receive key event immediately when same key up.
window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
}
@@ -5503,8 +5511,8 @@
* FLAG_WINDOW_IS_PARTIALLY_OBSCURED.
*/
TEST_F(InputDispatcherTest, SlipperyWindow_SetsFlagPartiallyObscured) {
- constexpr int32_t SLIPPERY_PID = WINDOW_PID + 1;
- constexpr int32_t SLIPPERY_UID = WINDOW_UID + 1;
+ constexpr gui::Pid SLIPPERY_PID{WINDOW_PID.val() + 1};
+ constexpr gui::Uid SLIPPERY_UID{WINDOW_UID.val() + 1};
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -5585,24 +5593,25 @@
}
TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithMotions) {
+ using Uid = gui::Uid;
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
leftWindow->setFrame(Rect(0, 0, 100, 100));
- leftWindow->setOwnerInfo(1, 101);
+ leftWindow->setOwnerInfo(gui::Pid{1}, Uid{101});
sp<FakeWindowHandle> rightSpy =
sp<FakeWindowHandle>::make(application, mDispatcher, "Right spy", ADISPLAY_ID_DEFAULT);
rightSpy->setFrame(Rect(100, 0, 200, 100));
- rightSpy->setOwnerInfo(2, 102);
+ rightSpy->setOwnerInfo(gui::Pid{2}, Uid{102});
rightSpy->setSpy(true);
rightSpy->setTrustedOverlay(true);
sp<FakeWindowHandle> rightWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
rightWindow->setFrame(Rect(100, 0, 200, 100));
- rightWindow->setOwnerInfo(3, 103);
+ rightWindow->setOwnerInfo(gui::Pid{3}, Uid{103});
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {rightSpy, rightWindow, leftWindow}}});
@@ -5612,7 +5621,8 @@
.build());
ASSERT_NO_FATAL_FAILURE(leftWindow->consumeMotionDown());
mDispatcher->waitForIdle();
- ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID, {101}));
+ ASSERT_NO_FATAL_FAILURE(
+ mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID, {Uid{101}}));
// Touch another finger over the right windows
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
@@ -5624,7 +5634,8 @@
ASSERT_NO_FATAL_FAILURE(leftWindow->consumeMotionMove());
mDispatcher->waitForIdle();
ASSERT_NO_FATAL_FAILURE(
- mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID, {101, 102, 103}));
+ mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID,
+ {Uid{101}, Uid{102}, Uid{103}}));
// Release finger over left window. The UP actions are not treated as device interaction.
// The windows that did not receive the UP pointer will receive MOVE events, but since this
@@ -5647,7 +5658,7 @@
ASSERT_NO_FATAL_FAILURE(rightWindow->consumeMotionMove());
mDispatcher->waitForIdle();
ASSERT_NO_FATAL_FAILURE(
- mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID, {102, 103}));
+ mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID, {Uid{102}, Uid{103}}));
// Release all fingers
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
@@ -5665,7 +5676,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- window->setOwnerInfo(1, 101);
+ window->setOwnerInfo(gui::Pid{1}, gui::Uid{101});
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
setFocusedWindow(window);
@@ -5674,7 +5685,8 @@
mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build());
ASSERT_NO_FATAL_FAILURE(window->consumeKeyDown(ADISPLAY_ID_DEFAULT));
mDispatcher->waitForIdle();
- ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID, {101}));
+ ASSERT_NO_FATAL_FAILURE(
+ mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID, {gui::Uid{101}}));
// The UP actions are not treated as device interaction.
mDispatcher->notifyKey(KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).build());
@@ -6060,7 +6072,7 @@
mDispatcher->notifyMotion(motionArgs);
ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
- const auto xy = transform.transform(motionArgs.pointerCoords->getXYValue());
+ const auto xy = transform.transform(motionArgs.pointerCoords[0].getXYValue());
mFakePolicy->assertFilterInputEventWasCalled(motionArgs, xy);
} else {
mFakePolicy->assertFilterInputEventWasNotCalled();
@@ -7871,9 +7883,9 @@
static_assert(1 - (1 - OPACITY_FAR_BELOW_THRESHOLD) * (1 - OPACITY_FAR_BELOW_THRESHOLD) <
MAXIMUM_OBSCURING_OPACITY);
- static const int32_t TOUCHED_APP_UID = 10001;
- static const int32_t APP_B_UID = 10002;
- static const int32_t APP_C_UID = 10003;
+ static constexpr gui::Uid TOUCHED_APP_UID{10001};
+ static constexpr gui::Uid APP_B_UID{10002};
+ static constexpr gui::Uid APP_C_UID{10003};
sp<FakeWindowHandle> mTouchWindow;
@@ -7888,7 +7900,7 @@
mTouchWindow.clear();
}
- sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name, TouchOcclusionMode mode,
+ sp<FakeWindowHandle> getOccludingWindow(gui::Uid uid, std::string name, TouchOcclusionMode mode,
float alpha = 1.0f) {
sp<FakeWindowHandle> window = getWindow(uid, name);
window->setTouchable(false);
@@ -7897,12 +7909,12 @@
return window;
}
- sp<FakeWindowHandle> getWindow(int32_t uid, std::string name) {
+ sp<FakeWindowHandle> getWindow(gui::Uid uid, std::string name) {
std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(app, mDispatcher, name, ADISPLAY_ID_DEFAULT);
// Generate an arbitrary PID based on the UID
- window->setOwnerInfo(1777 + (uid % 10000), uid);
+ window->setOwnerInfo(gui::Pid{static_cast<pid_t>(1777 + (uid.val() % 10000))}, uid);
return window;
}
@@ -8441,7 +8453,7 @@
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -8472,7 +8484,7 @@
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
- mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
// nothing to the window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -8518,7 +8530,7 @@
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- mFakePolicy->assertDropTargetEquals(nullptr);
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, nullptr);
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -8601,7 +8613,7 @@
injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- mFakePolicy->assertDropTargetEquals(mWindow->getToken());
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->consumeMotionMove();
}
@@ -8651,7 +8663,7 @@
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -8700,7 +8712,7 @@
.build()))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -8724,6 +8736,8 @@
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT));
window->assertNoEvents();
// With the flag cleared, the window should get input
@@ -8746,13 +8760,13 @@
sp<FakeWindowHandle>::make(obscuringApplication, mDispatcher, "obscuringWindow",
ADISPLAY_ID_DEFAULT);
obscuringWindow->setFrame(Rect(0, 0, 50, 50));
- obscuringWindow->setOwnerInfo(111, 111);
+ obscuringWindow->setOwnerInfo(gui::Pid{111}, gui::Uid{111});
obscuringWindow->setTouchable(false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Test window", ADISPLAY_ID_DEFAULT);
window->setDropInputIfObscured(true);
- window->setOwnerInfo(222, 222);
+ window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
@@ -8765,6 +8779,8 @@
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT));
window->assertNoEvents();
// With the flag cleared, the window should get input
@@ -8787,13 +8803,13 @@
sp<FakeWindowHandle>::make(obscuringApplication, mDispatcher, "obscuringWindow",
ADISPLAY_ID_DEFAULT);
obscuringWindow->setFrame(Rect(0, 0, 50, 50));
- obscuringWindow->setOwnerInfo(111, 111);
+ obscuringWindow->setOwnerInfo(gui::Pid{111}, gui::Uid{111});
obscuringWindow->setTouchable(false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
"Test window", ADISPLAY_ID_DEFAULT);
window->setDropInputIfObscured(true);
- window->setOwnerInfo(222, 222);
+ window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
@@ -8806,6 +8822,8 @@
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT));
window->assertNoEvents();
// When the window is no longer obscured because it went on top, it should get input
@@ -8868,7 +8886,7 @@
}
}
- void changeAndVerifyTouchModeInMainDisplayOnly(bool inTouchMode, int32_t pid, int32_t uid,
+ void changeAndVerifyTouchModeInMainDisplayOnly(bool inTouchMode, gui::Pid pid, gui::Uid uid,
bool hasPermission) {
ASSERT_TRUE(mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission,
ADISPLAY_ID_DEFAULT));
@@ -8887,9 +8905,9 @@
TEST_F(InputDispatcherTouchModeChangedTests, NonFocusedWindowOwnerCannotChangeTouchMode) {
const WindowInfo& windowInfo = *mWindow->getInfo();
- int32_t ownerPid = windowInfo.ownerPid;
- int32_t ownerUid = windowInfo.ownerUid;
- mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1);
+ gui::Pid ownerPid = windowInfo.ownerPid;
+ gui::Uid ownerUid = windowInfo.ownerUid;
+ mWindow->setOwnerInfo(gui::Pid::INVALID, gui::Uid::INVALID);
ASSERT_FALSE(mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, ownerPid,
ownerUid, /*hasPermission=*/false,
ADISPLAY_ID_DEFAULT));
@@ -8899,9 +8917,9 @@
TEST_F(InputDispatcherTouchModeChangedTests, NonWindowOwnerMayChangeTouchModeOnPermissionGranted) {
const WindowInfo& windowInfo = *mWindow->getInfo();
- int32_t ownerPid = windowInfo.ownerPid;
- int32_t ownerUid = windowInfo.ownerUid;
- mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1);
+ gui::Pid ownerPid = windowInfo.ownerPid;
+ gui::Uid ownerUid = windowInfo.ownerUid;
+ mWindow->setOwnerInfo(gui::Pid::INVALID, gui::Uid::INVALID);
changeAndVerifyTouchModeInMainDisplayOnly(!InputDispatcher::kDefaultInTouchMode, ownerPid,
ownerUid, /*hasPermission=*/true);
}
@@ -9112,10 +9130,10 @@
*/
TEST_F(InputDispatcherSpyWindowTest, WatchOutsideTouches) {
auto window = createForeground();
- window->setOwnerInfo(12, 34);
+ window->setOwnerInfo(gui::Pid{12}, gui::Uid{34});
auto spy = createSpy();
spy->setWatchOutsideTouch(true);
- spy->setOwnerInfo(56, 78);
+ spy->setOwnerInfo(gui::Pid{56}, gui::Uid{78});
spy->setFrame(Rect{0, 0, 20, 20});
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
@@ -9528,7 +9546,7 @@
sp<FakeWindowHandle>::make(overlayApplication, mDispatcher,
"Stylus interceptor window", ADISPLAY_ID_DEFAULT);
overlay->setFocusable(false);
- overlay->setOwnerInfo(111, 111);
+ overlay->setOwnerInfo(gui::Pid{111}, gui::Uid{111});
overlay->setTouchable(false);
overlay->setInterceptsStylus(true);
overlay->setTrustedOverlay(true);
@@ -9539,7 +9557,7 @@
sp<FakeWindowHandle>::make(application, mDispatcher, "Application window",
ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- window->setOwnerInfo(222, 222);
+ window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
@@ -9566,6 +9584,7 @@
using InputDispatcherStylusInterceptorDeathTest = InputDispatcherStylusInterceptorTest;
TEST_F(InputDispatcherStylusInterceptorDeathTest, UntrustedOverlay_AbortsDispatcher) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
ScopedSilentDeath _silentDeath;
auto [overlay, window] = setupStylusOverlayScenario();
@@ -9649,12 +9668,12 @@
}
struct User {
- int32_t mPid;
- int32_t mUid;
+ gui::Pid mPid;
+ gui::Uid mUid;
uint32_t mPolicyFlags{DEFAULT_POLICY_FLAGS};
std::unique_ptr<InputDispatcher>& mDispatcher;
- User(std::unique_ptr<InputDispatcher>& dispatcher, int32_t pid, int32_t uid)
+ User(std::unique_ptr<InputDispatcher>& dispatcher, gui::Pid pid, gui::Uid uid)
: mPid(pid), mUid(uid), mDispatcher(dispatcher) {}
InputEventInjectionResult injectTargetedMotion(int32_t action) const {
@@ -9687,7 +9706,7 @@
using InputDispatcherTargetedInjectionTest = InputDispatcherTest;
TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoOwnedWindow) {
- auto owner = User(mDispatcher, 10, 11);
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -9704,11 +9723,11 @@
}
TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedWindow) {
- auto owner = User(mDispatcher, 10, 11);
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- auto rando = User(mDispatcher, 20, 21);
+ auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
EXPECT_EQ(InputEventInjectionResult::TARGET_MISMATCH,
rando.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
@@ -9721,7 +9740,7 @@
}
TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoOwnedSpyWindow) {
- auto owner = User(mDispatcher, 10, 11);
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
auto spy = owner.createWindow();
spy->setSpy(true);
@@ -9735,10 +9754,10 @@
}
TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedSpyWindow) {
- auto owner = User(mDispatcher, 10, 11);
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
- auto rando = User(mDispatcher, 20, 21);
+ auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
auto randosSpy = rando.createWindow();
randosSpy->setSpy(true);
randosSpy->setTrustedOverlay(true);
@@ -9753,10 +9772,10 @@
}
TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoAnyWindowWhenNotTargeting) {
- auto owner = User(mDispatcher, 10, 11);
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
- auto rando = User(mDispatcher, 20, 21);
+ auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
auto randosSpy = rando.createWindow();
randosSpy->setSpy(true);
randosSpy->setTrustedOverlay(true);
@@ -9778,10 +9797,10 @@
}
TEST_F(InputDispatcherTargetedInjectionTest, CannotGenerateActionOutsideToOtherUids) {
- auto owner = User(mDispatcher, 10, 11);
+ auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
auto window = owner.createWindow();
- auto rando = User(mDispatcher, 20, 21);
+ auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
auto randosWindow = rando.createWindow();
randosWindow->setFrame(Rect{-10, -10, -5, -5});
randosWindow->setWatchOutsideTouch(true);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 327513d..d1c3f7d 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1534,8 +1534,8 @@
NotifyMotionArgs args;
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
EXPECT_EQ(action, args.action);
- ASSERT_EQ(points.size(), args.pointerCount);
- for (size_t i = 0; i < args.pointerCount; i++) {
+ ASSERT_EQ(points.size(), args.getPointerCount());
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
EXPECT_EQ(points[i].x, args.pointerCoords[i].getX());
EXPECT_EQ(points[i].y, args.pointerCoords[i].getY());
}
@@ -3949,7 +3949,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(uint32_t(1), args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
@@ -3967,7 +3967,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(uint32_t(1), args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
@@ -3988,7 +3988,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(0, args.buttonState);
ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(uint32_t(1), args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
@@ -4006,7 +4006,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(0, args.buttonState);
ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(uint32_t(1), args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
@@ -5269,7 +5269,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5293,7 +5293,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5316,7 +5316,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5366,7 +5366,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5389,7 +5389,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5434,7 +5434,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5461,7 +5461,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5486,7 +5486,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5529,7 +5529,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5554,7 +5554,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5577,7 +5577,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -5903,6 +5903,40 @@
mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
}
+TEST_F(SingleTouchInputMapperTest, Process_DoesntCheckPhysicalFrameForTouchpads) {
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(fakePointerController);
+
+ addConfigurationProperty("touch.deviceType", "pointer");
+ prepareAxes(POSITION);
+ prepareDisplay(ui::ROTATION_0);
+ auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
+
+ // Set a physical frame in the display viewport.
+ auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ viewport->physicalLeft = 20;
+ viewport->physicalTop = 600;
+ viewport->physicalRight = 30;
+ viewport->physicalBottom = 610;
+ mFakePolicy->updateViewport(*viewport);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ // Start the touch.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 1);
+ processSync(mapper);
+
+ // Expect all input starting outside the physical frame to result in NotifyMotionArgs being
+ // produced.
+ const std::array<Point, 6> outsidePoints = {
+ {{0, 0}, {19, 605}, {31, 605}, {25, 599}, {25, 611}, {DISPLAY_WIDTH, DISPLAY_HEIGHT}}};
+ for (const auto& p : outsidePoints) {
+ processMove(mapper, toRawX(p.x), toRawY(p.y));
+ processSync(mapper);
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+ }
+}
+
TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
@@ -6898,7 +6932,7 @@
NotifyMotionArgs motionArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], point.x, point.y,
1, 0, 0, 0, 0, 0, 0, 0));
}
@@ -6970,7 +7004,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_NO_FATAL_FAILURE(
assertPointerCoords(motionArgs.pointerCoords[0], 11, 21, 1, 0, 0, 0, 0, 0, 0, 0));
@@ -7758,7 +7792,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -7777,7 +7811,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -7808,7 +7842,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -7837,7 +7871,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -7860,7 +7894,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -7885,7 +7919,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -7912,7 +7946,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -7941,7 +7975,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -7964,7 +7998,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -7987,7 +8021,7 @@
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, motionArgs.edgeFlags);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8075,7 +8109,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8083,7 +8117,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8105,7 +8139,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8124,7 +8158,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_0_UP, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8136,7 +8170,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8151,7 +8185,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8169,7 +8203,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_0_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8188,7 +8222,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_UP, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8200,7 +8234,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8212,7 +8246,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8245,7 +8279,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8253,7 +8287,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8273,7 +8307,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8293,7 +8327,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_0_UP, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8305,7 +8339,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8318,7 +8352,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8334,7 +8368,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_0_DOWN, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8354,7 +8388,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_UP, motionArgs.action);
- ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(2), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
@@ -8366,7 +8400,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8378,7 +8412,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(size_t(1), motionArgs.getPointerCount());
ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
@@ -8519,7 +8553,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
- ASSERT_EQ(size_t(2), args.pointerCount);
+ ASSERT_EQ(size_t(2), args.getPointerCount());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
x, y, 1.0f, size, touch, touch, tool, tool, 0, 0));
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[1],
@@ -9778,7 +9812,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// First finger up. It used to be in palm mode, and we already generated ACTION_POINTER_UP for
// it. Second finger receive move.
@@ -9787,7 +9821,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// Second finger keeps moving.
processSlot(mapper, SECOND_SLOT);
@@ -9796,7 +9830,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// Second finger up.
processId(mapper, INVALID_TRACKING_ID);
@@ -9870,7 +9904,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// third finger move
processId(mapper, THIRD_TRACKING_ID);
@@ -9885,7 +9919,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// second finger up, third finger receive move.
processSlot(mapper, SECOND_SLOT);
@@ -9893,7 +9927,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// third finger up.
processSlot(mapper, THIRD_SLOT);
@@ -9950,7 +9984,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// second finger up.
processSlot(mapper, SECOND_SLOT);
@@ -9996,7 +10030,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// First finger move.
processId(mapper, FIRST_TRACKING_ID);
@@ -10005,7 +10039,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
// Second finger down.
processSlot(mapper, SECOND_SLOT);
@@ -10015,7 +10049,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
- ASSERT_EQ(uint32_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(2), motionArgs.getPointerCount());
// second finger up with some unexpected data.
processSlot(mapper, SECOND_SLOT);
@@ -10024,7 +10058,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ACTION_POINTER_1_UP, motionArgs.action);
- ASSERT_EQ(uint32_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(2), motionArgs.getPointerCount());
// first finger up with some unexpected data.
processSlot(mapper, FIRST_SLOT);
@@ -10034,7 +10068,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
}
TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
@@ -10289,7 +10323,7 @@
NotifyMotionArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(1U, args.pointerCount);
+ ASSERT_EQ(1U, args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, args.source);
ASSERT_NO_FATAL_FAILURE(
@@ -10304,7 +10338,7 @@
// expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
- ASSERT_EQ(2U, args.pointerCount);
+ ASSERT_EQ(2U, args.getPointerCount());
ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_EQ(1, args.pointerProperties[1].id);
ASSERT_NO_FATAL_FAILURE(
@@ -10372,7 +10406,7 @@
// expect coord[0] to contain new location of touch 1, and properties[0].id to contain 1
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_EQ(1U, args.pointerCount);
+ ASSERT_EQ(1U, args.getPointerCount());
ASSERT_EQ(1, args.pointerProperties[0].id);
ASSERT_NO_FATAL_FAILURE(
assertPointerCoords(args.pointerCoords[0], 320, 900, 1, 0, 0, 0, 0, 0, 0, 0));
@@ -10592,7 +10626,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
@@ -10614,7 +10648,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
@@ -10652,7 +10686,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
@@ -10674,7 +10708,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
@@ -10708,7 +10742,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
@@ -10733,16 +10767,16 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
// The previous PRESS gesture is cancelled, because it is transformed to freeform
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
- ASSERT_EQ(2U, motionArgs.pointerCount);
+ ASSERT_EQ(2U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN, motionArgs.action & AMOTION_EVENT_ACTION_MASK);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
@@ -10772,7 +10806,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(2U, motionArgs.pointerCount);
+ ASSERT_EQ(2U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
@@ -10801,7 +10835,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
ASSERT_EQ(0, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET));
@@ -10823,7 +10857,7 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(1U, motionArgs.getPointerCount());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
ASSERT_LT(motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET), 0);
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 01b79ca..70bad7c 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -71,8 +71,8 @@
}
MATCHER_P(WithPointerCount, count, "MotionEvent with specified number of pointers") {
- *result_listener << "expected " << count << " pointer(s), but got " << arg.pointerCount;
- return arg.pointerCount == count;
+ *result_listener << "expected " << count << " pointer(s), but got " << arg.getPointerCount();
+ return arg.getPointerCount() == count;
}
MATCHER_P2(WithPointerId, index, id, "MotionEvent with specified pointer ID for pointer index") {
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index 1fff2c7..da0815f 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -138,9 +138,10 @@
static void assertArgs(const NotifyMotionArgs& args, int32_t action,
const std::vector<std::pair<int32_t /*pointerId*/, PointerData>>& pointers) {
- ASSERT_EQ(action, args.action);
- ASSERT_EQ(pointers.size(), args.pointerCount);
- for (size_t i = 0; i < args.pointerCount; i++) {
+ ASSERT_EQ(action, args.action)
+ << "Expected " << MotionEvent::actionToString(action) << " but got " << args.action;
+ ASSERT_EQ(pointers.size(), args.getPointerCount());
+ for (size_t i = 0; i < args.getPointerCount(); i++) {
const auto& [pointerId, pointerData] = pointers[i];
ASSERT_EQ(pointerId, args.pointerProperties[i].id);
ASSERT_EQ(pointerData.x, args.pointerCoords[i].getX());
@@ -196,7 +197,7 @@
AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}});
NotifyMotionArgs noPointers = removePointerIds(args, {0, 1});
- ASSERT_EQ(0u, noPointers.pointerCount);
+ ASSERT_EQ(0u, noPointers.getPointerCount());
}
/**
@@ -771,7 +772,7 @@
ASSERT_EQ(POINTER_0_UP, argsList[0].action);
ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
ASSERT_EQ(MOVE, argsList[1].action);
- ASSERT_EQ(1u, argsList[1].pointerCount);
+ ASSERT_EQ(1u, argsList[1].getPointerCount());
ASSERT_EQ(0, argsList[1].flags);
mPalmRejector->processMotion(
@@ -958,7 +959,7 @@
{{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(MOVE, argsList[0].action);
- ASSERT_EQ(1u, argsList[0].pointerCount);
+ ASSERT_EQ(1u, argsList[0].getPointerCount());
ASSERT_EQ(1433, argsList[0].pointerCoords[0].getX());
ASSERT_EQ(751, argsList[0].pointerCoords[0].getY());
}
@@ -986,7 +987,7 @@
ASSERT_EQ(1u, argsList.size());
// Cancel all
ASSERT_EQ(CANCEL, argsList[0].action);
- ASSERT_EQ(2u, argsList[0].pointerCount);
+ ASSERT_EQ(2u, argsList[0].getPointerCount());
ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
// Future move events are ignored
@@ -1001,7 +1002,7 @@
{{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}, {1000, 700, 10}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(DOWN, argsList[0].action);
- ASSERT_EQ(1u, argsList[0].pointerCount);
+ ASSERT_EQ(1u, argsList[0].getPointerCount());
ASSERT_EQ(2, argsList[0].pointerProperties[0].id);
}
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index 55c2db6..47b0824 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -21,52 +21,35 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_fuzz {
- name: "inputflinger_latencytracker_fuzzer",
- defaults: [
- "inputflinger_defaults",
- ],
- include_dirs: [
- "frameworks/native/services/inputflinger",
- ],
- shared_libs: [
- "libbase",
- "libbinder",
- "liblog",
- "libutils",
- "libinput",
- "libinputflinger",
- ],
- srcs: [
- "LatencyTrackerFuzzer.cpp",
- ],
- fuzz_config: {
- cc: ["android-framework-input@google.com"],
- },
-}
-
cc_defaults {
name: "inputflinger_fuzz_defaults",
defaults: [
"inputflinger_defaults",
+ "libinputflinger_defaults",
],
+ host_supported: true,
include_dirs: [
"frameworks/native/services/inputflinger",
],
shared_libs: [
- "android.hardware.input.classifier@1.0",
- "android.hardware.input.processor-V1-ndk",
- "libbase",
- "libbinder",
- "libcutils",
- "liblog",
- "libutils",
- "libinput",
- "libinputflinger",
"libinputreader",
"libinputflinger_base",
- "libstatslog",
],
+ sanitize: {
+ hwaddress: true,
+ undefined: true,
+ all_undefined: true,
+ diag: {
+ undefined: true,
+ },
+ },
+ target: {
+ host: {
+ sanitize: {
+ address: true,
+ },
+ },
+ },
header_libs: [
"libbatteryservice_headers",
"libinputreader_headers",
@@ -145,3 +128,17 @@
"InputClassifierFuzzer.cpp",
],
}
+
+cc_fuzz {
+ name: "inputflinger_latencytracker_fuzzer",
+ defaults: [
+ "inputflinger_fuzz_defaults",
+ "libinputdispatcher_defaults",
+ ],
+ shared_libs: [
+ "libinputreporter",
+ ],
+ srcs: [
+ "LatencyTrackerFuzzer.cpp",
+ ],
+}
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 398d602..90d7541 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1055,7 +1055,12 @@
if (count < 0) {
if(count == DEAD_OBJECT && device.isReconnecting()) {
device.reconnect();
- continue;
+ // There are no "real" events at this point, but do not skip the rest of the loop
+ // if there are pending runtime events.
+ Mutex::Autolock _l(&mLock);
+ if (mRuntimeSensorEventQueue.empty()) {
+ continue;
+ }
} else {
ALOGE("sensor poll failed (%s)", strerror(-count));
break;
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 0aa1bcb..545f6c2 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -61,7 +61,7 @@
// For older HALs which don't support batching, use a smaller socket buffer size.
#define SOCKET_BUFFER_SIZE_NON_BATCHED (4 * 1024)
-#define SENSOR_REGISTRATIONS_BUF_SIZE 200
+#define SENSOR_REGISTRATIONS_BUF_SIZE 500
// Apps that targets S+ and do not have HIGH_SAMPLING_RATE_SENSORS permission will be capped
// at 200 Hz. The cap also applies to all requests when the mic toggle is flipped to on, regardless
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index d93e25e..09bc467 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -37,6 +37,15 @@
half4 color;
std::vector<int32_t> layerIds;
};
+
+// Interface of composition engine power hint callback.
+struct ICEPowerCallback {
+ virtual void notifyCpuLoadUp() = 0;
+
+protected:
+ ~ICEPowerCallback() = default;
+};
+
/**
* A parameter object for refreshing a set of outputs
*/
@@ -96,6 +105,8 @@
std::vector<BorderRenderInfo> borderInfoList;
bool hasTrustedPresentationListener = false;
+
+ ICEPowerCallback* powerCallback = nullptr;
};
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index a3fda61..28c6e92 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -32,6 +32,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/ProjectionSpace.h>
#include <renderengine/BorderRenderInfo.h>
#include <ui/LayerStack.h>
@@ -167,6 +168,8 @@
uint64_t lastOutputLayerHash = 0;
uint64_t outputLayerHash = 0;
+ ICEPowerCallback* powerCallback = nullptr;
+
// Debugging
void dump(std::string& result) const;
};
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 793959c..1205a2c 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -843,6 +843,7 @@
editState().earliestPresentTime = refreshArgs.earliestPresentTime;
editState().expectedPresentTime = refreshArgs.expectedPresentTime;
+ editState().powerCallback = refreshArgs.powerCallback;
compositionengine::OutputLayer* peekThroughLayer = nullptr;
sp<GraphicBuffer> previousOverride = nullptr;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 8ced0ac..a6521bb 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -162,6 +162,9 @@
const OutputCompositionState& outputState,
bool deviceHandlesColorTransform) {
ATRACE_CALL();
+ if (outputState.powerCallback) {
+ outputState.powerCallback->notifyCpuLoadUp();
+ }
const Rect& viewport = outputState.layerStackSpace.getContent();
const ui::Dataspace& outputDataspace = outputState.dataspace;
const ui::Transform::RotationFlags orientation =
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index 961ec80..f74ef4c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -34,6 +34,7 @@
MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
(override));
MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
+ MOCK_METHOD(void, notifyCpuLoadUp, (), (override));
MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index f7049b9..c0eb36d 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -20,6 +20,7 @@
#include "AidlComposerHal.h"
+#include <SurfaceFlingerProperties.h>
#include <android-base/file.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
@@ -249,15 +250,18 @@
ALOGE("getInterfaceVersion for AidlComposer constructor failed %s",
status.getDescription().c_str());
}
- if (version == 1) {
- mClearSlotBuffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
- GraphicBuffer::USAGE_HW_COMPOSER |
- GraphicBuffer::USAGE_SW_READ_OFTEN |
- GraphicBuffer::USAGE_SW_WRITE_OFTEN,
- "AidlComposer");
- if (!mClearSlotBuffer || mClearSlotBuffer->initCheck() != ::android::OK) {
- LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots");
- return;
+ mSupportsBufferSlotsToClear = version > 1;
+ if (!mSupportsBufferSlotsToClear) {
+ if (sysprop::clear_slots_with_set_layer_buffer(false)) {
+ mClearSlotBuffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_SW_READ_OFTEN |
+ GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+ "AidlComposer");
+ if (!mClearSlotBuffer || mClearSlotBuffer->initCheck() != ::android::OK) {
+ LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots");
+ return;
+ }
}
}
@@ -844,12 +848,12 @@
Error error = Error::NONE;
mMutex.lock_shared();
if (auto writer = getWriter(display)) {
- // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder
- // buffer, using the slot that needs to cleared... tricky.
- if (mClearSlotBuffer == nullptr) {
+ if (mSupportsBufferSlotsToClear) {
writer->get().setLayerBufferSlotsToClear(translate<int64_t>(display),
translate<int64_t>(layer), slotsToClear);
- } else {
+ // Backwards compatible way of clearing buffer slots is to set the layer buffer with a
+ // placeholder buffer, using the slot that needs to cleared... tricky.
+ } else if (mClearSlotBuffer != nullptr) {
for (uint32_t slot : slotsToClear) {
// Don't clear the active buffer slot because we need to restore the active buffer
// after clearing the requested buffer slots with a placeholder buffer.
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index ded91be..8d21b49 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -285,6 +285,8 @@
// threading annotations.
ftl::SharedMutex mMutex;
+ // Whether or not explicitly clearing buffer slots is supported.
+ bool mSupportsBufferSlotsToClear;
// Buffer slots for layers are cleared by setting the slot buffer to this buffer.
sp<GraphicBuffer> mClearSlotBuffer;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index e0f6c45..9b41da5 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -24,12 +24,14 @@
#include "HidlComposerHal.h"
+#include <SurfaceFlingerProperties.h>
#include <android/binder_manager.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <hidl/HidlTransportSupport.h>
#include <hidl/HidlTransportUtils.h>
#include <log/log.h>
#include <utils/Trace.h>
+
#include "HWC2.h"
#include "Hal.h"
@@ -189,6 +191,9 @@
}
sp<GraphicBuffer> allocateClearSlotBuffer() {
+ if (!sysprop::clear_slots_with_set_layer_buffer(false)) {
+ return nullptr;
+ }
sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
GraphicBuffer::USAGE_HW_COMPOSER |
GraphicBuffer::USAGE_SW_READ_OFTEN |
@@ -246,7 +251,7 @@
LOG_ALWAYS_FATAL("failed to create composer client");
}
- if (!mClearSlotBuffer) {
+ if (!mClearSlotBuffer && sysprop::clear_slots_with_set_layer_buffer(false)) {
LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots");
return;
}
@@ -716,7 +721,11 @@
if (slotsToClear.empty()) {
return Error::NONE;
}
- // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder
+ // This can be null when the HAL hasn't explicitly enabled this feature.
+ if (mClearSlotBuffer == nullptr) {
+ return Error::NONE;
+ }
+ // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder
// buffer, using the slot that needs to cleared... tricky.
for (uint32_t slot : slotsToClear) {
// Don't clear the active buffer slot because we need to restore the active buffer after
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index f8b466c..9c7576e 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -138,6 +138,21 @@
}
}
+void PowerAdvisor::notifyCpuLoadUp() {
+ // Only start sending this notification once the system has booted so we don't introduce an
+ // early-boot dependency on Power HAL
+ if (!mBootFinished.load()) {
+ return;
+ }
+ if (usePowerHintSession() && ensurePowerHintSessionRunning()) {
+ std::lock_guard lock(mHintSessionMutex);
+ auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_UP);
+ if (!ret.isOk()) {
+ mHintSessionRunning = false;
+ }
+ }
+}
+
void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
// Only start sending this notification once the system has booted so we don't introduce an
// early-boot dependency on Power HAL
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index f0d3fd8..cfaa135 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -49,6 +49,7 @@
virtual void onBootFinished() = 0;
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual bool isUsingExpensiveRendering() = 0;
+ virtual void notifyCpuLoadUp() = 0;
virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
// Checks both if it supports and if it's enabled
virtual bool usePowerHintSession() = 0;
@@ -108,6 +109,7 @@
void onBootFinished() override;
void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
+ void notifyCpuLoadUp() override;
void notifyDisplayUpdateImminentAndCpuReset() override;
bool usePowerHintSession() override;
bool supportsPowerHintSession() override;
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 5913d4b..163d345 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -16,7 +16,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
-#define LOG_TAG "LayerHierarchy"
+#define LOG_TAG "SurfaceFlinger"
#include "LayerHierarchy.h"
#include "LayerLog.h"
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index b25b731..5389ada 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -42,10 +42,10 @@
class LayerHierarchy {
public:
enum Variant : uint32_t {
- Attached,
- Detached,
- Relative,
- Mirror,
+ Attached, // child of the parent
+ Detached, // child of the parent but currently relative parented to another layer
+ Relative, // relative child of the parent
+ Mirror, // mirrored from another layer
ftl_first = Attached,
ftl_last = Mirror,
};
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index cd9515c..1712137 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -17,7 +17,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
-#define LOG_TAG "LayerLifecycleManager"
+#define LOG_TAG "SurfaceFlinger"
#include "LayerLifecycleManager.h"
#include "Client.h" // temporarily needed for LayerCreationArgs
@@ -28,6 +28,14 @@
using namespace ftl::flag_operators;
+namespace {
+// Returns true if the layer is root of a display and can be mirrored by mirroringLayer
+bool canMirrorRootLayer(RequestedLayerState& mirroringLayer, RequestedLayerState& rootLayer) {
+ return rootLayer.isRoot() && rootLayer.layerStack == mirroringLayer.layerStackToMirror &&
+ rootLayer.id != mirroringLayer.id;
+}
+} // namespace
+
void LayerLifecycleManager::addLayers(std::vector<std::unique_ptr<RequestedLayerState>> newLayers) {
if (newLayers.empty()) {
return;
@@ -43,14 +51,19 @@
it->second.owner.getDebugString().c_str());
}
mAddedLayers.push_back(newLayer.get());
+ mChangedLayers.push_back(newLayer.get());
layer.parentId = linkLayer(layer.parentId, layer.id);
layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
if (layer.layerStackToMirror != ui::INVALID_LAYER_STACK) {
+ // Set mirror layer's default layer stack to -1 so it doesn't end up rendered on a
+ // display accidentally.
+ layer.layerStack = ui::INVALID_LAYER_STACK;
+
// if this layer is mirroring a display, then walk though all the existing root layers
// for the layer stack and add them as children to be mirrored.
mDisplayMirroringLayers.emplace_back(layer.id);
for (auto& rootLayer : mLayers) {
- if (rootLayer->isRoot() && rootLayer->layerStack == layer.layerStackToMirror) {
+ if (canMirrorRootLayer(layer, *rootLayer)) {
layer.mirrorIds.emplace_back(rootLayer->id);
linkLayer(rootLayer->id, layer.id);
}
@@ -190,6 +203,10 @@
continue;
}
+ if (layer->changes.get() == 0) {
+ mChangedLayers.push_back(layer);
+ }
+
if (transaction.flags & ISurfaceComposer::eAnimation) {
layer->changes |= RequestedLayerState::Changes::Animation;
}
@@ -232,6 +249,7 @@
bgColorLayer->what |= layer_state_t::eColorChanged |
layer_state_t::eDataspaceChanged | layer_state_t::eAlphaChanged;
bgColorLayer->changes |= RequestedLayerState::Changes::Content;
+ mChangedLayers.push_back(bgColorLayer);
mGlobalChanges |= RequestedLayerState::Changes::Content;
}
}
@@ -278,6 +296,7 @@
}
}
mDestroyedLayers.clear();
+ mChangedLayers.clear();
mGlobalChanges.clear();
}
@@ -298,10 +317,25 @@
return mDestroyedLayers;
}
+const std::vector<RequestedLayerState*>& LayerLifecycleManager::getChangedLayers() const {
+ return mChangedLayers;
+}
+
const ftl::Flags<RequestedLayerState::Changes> LayerLifecycleManager::getGlobalChanges() const {
return mGlobalChanges;
}
+const RequestedLayerState* LayerLifecycleManager::getLayerFromId(uint32_t id) const {
+ if (id == UNASSIGNED_LAYER_ID) {
+ return nullptr;
+ }
+ auto it = mIdToLayer.find(id);
+ if (it == mIdToLayer.end()) {
+ return nullptr;
+ }
+ return &it->second.owner;
+}
+
RequestedLayerState* LayerLifecycleManager::getLayerFromId(uint32_t id) {
if (id == UNASSIGNED_LAYER_ID) {
return nullptr;
@@ -383,10 +417,9 @@
// and updates its list of layers that its mirroring. This function should be called when a new
// root layer is added, removed or moved to another display.
void LayerLifecycleManager::updateDisplayMirrorLayers(RequestedLayerState& rootLayer) {
- for (uint32_t mirrorLayerId : mDisplayMirroringLayers) {
- RequestedLayerState* mirrorLayer = getLayerFromId(mirrorLayerId);
- bool canBeMirrored =
- rootLayer.isRoot() && rootLayer.layerStack == mirrorLayer->layerStackToMirror;
+ for (uint32_t mirroringLayerId : mDisplayMirroringLayers) {
+ RequestedLayerState* mirrorLayer = getLayerFromId(mirroringLayerId);
+ bool canBeMirrored = canMirrorRootLayer(*mirrorLayer, rootLayer);
bool currentlyMirrored =
std::find(mirrorLayer->mirrorIds.begin(), mirrorLayer->mirrorIds.end(),
rootLayer.id) != mirrorLayer->mirrorIds.end();
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index f0d2c22..48571bf 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -76,7 +76,9 @@
void removeLifecycleListener(std::shared_ptr<ILifecycleListener>);
const std::vector<std::unique_ptr<RequestedLayerState>>& getLayers() const;
const std::vector<std::unique_ptr<RequestedLayerState>>& getDestroyedLayers() const;
+ const std::vector<RequestedLayerState*>& getChangedLayers() const;
const ftl::Flags<RequestedLayerState::Changes> getGlobalChanges() const;
+ const RequestedLayerState* getLayerFromId(uint32_t) const;
private:
friend class LayerLifecycleManagerTest;
@@ -111,6 +113,8 @@
// Keeps track of all the layers that were added in order. Changes will be cleared once
// committed.
std::vector<RequestedLayerState*> mAddedLayers;
+ // Keeps track of new and layers with states changes since last commit.
+ std::vector<RequestedLayerState*> mChangedLayers;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index a992584..f0826c6 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -16,7 +16,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
-#define LOG_TAG "LayerSnapshot"
+#define LOG_TAG "SurfaceFlinger"
#include "LayerSnapshot.h"
@@ -24,6 +24,23 @@
using namespace ftl::flag_operators;
+namespace {
+
+void updateSurfaceDamage(const RequestedLayerState& requested, bool hasReadyFrame,
+ bool forceFullDamage, Region& outSurfaceDamageRegion) {
+ if (!hasReadyFrame) {
+ outSurfaceDamageRegion.clear();
+ return;
+ }
+ if (forceFullDamage) {
+ outSurfaceDamageRegion = Region::INVALID_REGION;
+ } else {
+ outSurfaceDamageRegion = requested.surfaceDamageRegion;
+ }
+}
+
+} // namespace
+
LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
const LayerHierarchy::TraversalPath& path)
: path(path) {
@@ -46,14 +63,16 @@
premultipliedAlpha = state.premultipliedAlpha;
inputInfo.name = state.name;
inputInfo.id = static_cast<int32_t>(uniqueSequence);
- inputInfo.ownerUid = static_cast<int32_t>(state.ownerUid);
- inputInfo.ownerPid = state.ownerPid;
+ inputInfo.ownerUid = gui::Uid{state.ownerUid};
+ inputInfo.ownerPid = gui::Pid{state.ownerPid};
uid = state.ownerUid;
pid = state.ownerPid;
changes = RequestedLayerState::Changes::Created;
+ clientChanges = 0;
mirrorRootPath = path.variant == LayerHierarchy::Variant::Mirror
? path
: LayerHierarchy::TraversalPath::ROOT;
+ reachablilty = LayerSnapshot::Reachablilty::Unreachable;
}
// As documented in libhardware header, formats in the range
@@ -131,6 +150,10 @@
}
bool LayerSnapshot::getIsVisible() const {
+ if (reachablilty != LayerSnapshot::Reachablilty::Reachable) {
+ return false;
+ }
+
if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) {
return false;
}
@@ -148,12 +171,16 @@
std::string LayerSnapshot::getIsVisibleReason() const {
// not visible
- if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot";
- if (!hasSomethingToDraw()) return "!hasSomethingToDraw";
- if (invalidTransform) return "invalidTransform";
+ if (reachablilty == LayerSnapshot::Reachablilty::Unreachable)
+ return "layer not reachable from root";
+ if (reachablilty == LayerSnapshot::Reachablilty::ReachableByRelativeParent)
+ return "layer only reachable via relative parent";
if (isHiddenByPolicyFromParent) return "hidden by parent or layer flag";
if (isHiddenByPolicyFromRelativeParent) return "hidden by relative parent";
+ if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot";
+ if (invalidTransform) return "invalidTransform";
if (color.a == 0.0f && !hasBlur()) return "alpha = 0 and no blur";
+ if (!hasSomethingToDraw()) return "!hasSomethingToDraw";
// visible
std::stringstream reason;
@@ -177,8 +204,9 @@
}
bool LayerSnapshot::hasInputInfo() const {
- return inputInfo.token != nullptr ||
- inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+ return (inputInfo.token != nullptr ||
+ inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)) &&
+ reachablilty == Reachablilty::Reachable;
}
std::string LayerSnapshot::getDebugString() const {
@@ -191,8 +219,16 @@
<< " geomLayerTransform={tx=" << geomLayerTransform.tx()
<< ",ty=" << geomLayerTransform.ty() << "}"
<< "}";
- debug << " input{ touchCropId=" << touchCropId
- << " replaceTouchableRegionWithCrop=" << inputInfo.replaceTouchableRegionWithCrop << "}";
+ if (hasInputInfo()) {
+ debug << " input{"
+ << "(" << inputInfo.inputConfig.string() << ")";
+ if (touchCropId != UNASSIGNED_LAYER_ID) debug << " touchCropId=" << touchCropId;
+ if (inputInfo.replaceTouchableRegionWithCrop) debug << " replaceTouchableRegionWithCrop";
+ auto touchableRegion = inputInfo.touchableRegion.getBounds();
+ debug << " touchableRegion={" << touchableRegion.left << "," << touchableRegion.top << ","
+ << touchableRegion.bottom << "," << touchableRegion.right << "}"
+ << "}";
+ }
return debug.str();
}
@@ -203,4 +239,172 @@
return geomBufferSize.toFloatRect();
}
+Hwc2::IComposerClient::BlendMode LayerSnapshot::getBlendMode(
+ const RequestedLayerState& requested) const {
+ auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
+ if (alpha != 1.0f || !contentOpaque) {
+ blendMode = requested.premultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED
+ : Hwc2::IComposerClient::BlendMode::COVERAGE;
+ }
+ return blendMode;
+}
+
+void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate,
+ bool displayChanges, bool forceFullDamage,
+ uint32_t displayRotationFlags) {
+ clientChanges = requested.what;
+ changes = requested.changes;
+ contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
+ // TODO(b/238781169) scope down the changes to only buffer updates.
+ hasReadyFrame = requested.hasReadyFrame();
+ sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
+ updateSurfaceDamage(requested, hasReadyFrame, forceFullDamage, surfaceDamage);
+
+ if (forceUpdate || requested.what & layer_state_t::eTransparentRegionChanged) {
+ transparentRegionHint = requested.transparentRegion;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eFlagsChanged) {
+ layerOpaqueFlagSet =
+ (requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eBufferTransformChanged) {
+ geomBufferTransform = requested.bufferTransform;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eTransformToDisplayInverseChanged) {
+ geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eDataspaceChanged) {
+ dataspace = requested.dataspace;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eExtendedRangeBrightnessChanged) {
+ currentHdrSdrRatio = requested.currentHdrSdrRatio;
+ desiredHdrSdrRatio = requested.desiredHdrSdrRatio;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eCachingHintChanged) {
+ cachingHint = requested.cachingHint;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eHdrMetadataChanged) {
+ hdrMetadata = requested.hdrMetadata;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eSidebandStreamChanged) {
+ sidebandStream = requested.sidebandStream;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged) {
+ shadowRadius = requested.shadowRadius;
+ shadowSettings.length = requested.shadowRadius;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eFrameRateSelectionPriority) {
+ frameRateSelectionPriority = requested.frameRateSelectionPriority;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eColorSpaceAgnosticChanged) {
+ isColorspaceAgnostic = requested.colorSpaceAgnostic;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eDimmingEnabledChanged) {
+ dimmingEnabled = requested.dimmingEnabled;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eCropChanged) {
+ geomCrop = requested.crop;
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eFlagsChanged | layer_state_t::eBufferChanged |
+ layer_state_t::eSidebandStreamChanged)) {
+ compositionType = requested.getCompositionType();
+ }
+
+ if (forceUpdate || requested.what & layer_state_t::eInputInfoChanged) {
+ if (requested.windowInfoHandle) {
+ inputInfo = *requested.windowInfoHandle->getInfo();
+ } else {
+ inputInfo = {};
+ // b/271132344 revisit this and see if we can always use the layers uid/pid
+ inputInfo.name = requested.name;
+ inputInfo.ownerUid = requested.ownerUid;
+ inputInfo.ownerPid = requested.ownerPid;
+ }
+ inputInfo.id = static_cast<int32_t>(uniqueSequence);
+ touchCropId = requested.touchCropId;
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eColorChanged | layer_state_t::eBufferChanged |
+ layer_state_t::eSidebandStreamChanged)) {
+ color.rgb = requested.getColor().rgb;
+ }
+
+ if (forceUpdate || requested.what & layer_state_t::eBufferChanged) {
+ acquireFence =
+ (requested.externalTexture &&
+ requested.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged))
+ ? requested.bufferData->acquireFence
+ : Fence::NO_FENCE;
+ buffer = requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
+ externalTexture = requested.externalTexture;
+ frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0;
+ hasProtectedContent = requested.externalTexture &&
+ requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
+ geomUsesSourceCrop = hasBufferOrSidebandStream();
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eCropChanged | layer_state_t::eBufferCropChanged |
+ layer_state_t::eBufferTransformChanged |
+ layer_state_t::eTransformToDisplayInverseChanged) ||
+ requested.changes.test(RequestedLayerState::Changes::BufferSize) || displayChanges) {
+ bufferSize = requested.getBufferSize(displayRotationFlags);
+ geomBufferSize = bufferSize;
+ croppedBufferSize = requested.getCroppedBufferSize(bufferSize);
+ geomContentCrop = requested.getBufferCrop();
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eFlagsChanged | layer_state_t::eDestinationFrameChanged |
+ layer_state_t::ePositionChanged | layer_state_t::eMatrixChanged |
+ layer_state_t::eBufferTransformChanged |
+ layer_state_t::eTransformToDisplayInverseChanged) ||
+ requested.changes.test(RequestedLayerState::Changes::BufferSize) || displayChanges) {
+ localTransform = requested.getTransform(displayRotationFlags);
+ localTransformInverse = localTransform.inverse();
+ }
+
+ if (forceUpdate || requested.what & (layer_state_t::eColorChanged) ||
+ requested.changes.test(RequestedLayerState::Changes::BufferSize)) {
+ color.rgb = requested.getColor().rgb;
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
+ layer_state_t::eApiChanged)) {
+ isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
+ requested.api == NATIVE_WINDOW_API_MEDIA &&
+ requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
+ layer_state_t::eApiChanged | layer_state_t::eShadowRadiusChanged |
+ layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged)) {
+ forceClientComposition = isHdrY410 || shadowSettings.length > 0 ||
+ requested.blurRegions.size() > 0 || stretchEffect.hasEffect();
+ }
+
+ if (forceUpdate ||
+ requested.what &
+ (layer_state_t::eColorChanged | layer_state_t::eShadowRadiusChanged |
+ layer_state_t::eBlurRegionsChanged | layer_state_t::eBackgroundBlurRadiusChanged |
+ layer_state_t::eCornerRadiusChanged | layer_state_t::eAlphaChanged |
+ layer_state_t::eFlagsChanged | layer_state_t::eBufferChanged |
+ layer_state_t::eSidebandStreamChanged)) {
+ contentOpaque = isContentOpaque();
+ isOpaque = contentOpaque && !roundedCorner.hasRoundedCorners() && color.a == 1.f;
+ blendMode = getBlendMode(requested);
+ }
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index b167d3e..2f45d52 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -18,6 +18,7 @@
#include <compositionengine/LayerFECompositionState.h>
#include <renderengine/LayerSettings.h>
+#include "DisplayHardware/ComposerHal.h"
#include "LayerHierarchy.h"
#include "RequestedLayerState.h"
#include "Scheduler/LayerInfo.h"
@@ -57,6 +58,7 @@
bool isHiddenByPolicyFromParent = false;
bool isHiddenByPolicyFromRelativeParent = false;
ftl::Flags<RequestedLayerState::Changes> changes;
+ uint64_t clientChanges = 0;
// Some consumers of this snapshot (input, layer traces) rely on each snapshot to be unique.
// For mirrored layers, snapshots will have the same sequence so this unique id provides
// an alternative identifier when needed.
@@ -93,11 +95,37 @@
bool handleSkipScreenshotFlag = false;
int32_t frameRateSelectionPriority;
LayerHierarchy::TraversalPath mirrorRootPath;
- bool unreachable = true;
uint32_t touchCropId;
- uid_t uid;
- pid_t pid;
+ gui::Uid uid = gui::Uid::INVALID;
+ gui::Pid pid = gui::Pid::INVALID;
ChildState childState;
+ enum class Reachablilty : uint32_t {
+ // Can traverse the hierarchy from a root node and reach this snapshot
+ Reachable,
+ // Cannot traverse the hierarchy from a root node and reach this snapshot
+ Unreachable,
+ // Can only reach this node from a relative parent. This means the nodes parents are
+ // not reachable.
+ // See example scenario:
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ └ - 111 (relative)
+ // │ ├── 13
+ // │ └── 14
+ // │ └ * 12 (mirroring)
+ // └── 2
+ // 111 will create two snapshots, first when visited from 1 -> 12 or 1 -> 11 and the
+ // second when visited from 1 -> 14 -> 12. Because its parent 11 doesn't exist in the
+ // mirrored hierarchy, the second snapshot will be marked as ReachableByRelativeParent.
+ // This snapshot doesn't have any valid properties because it cannot inherit from its
+ // parent. Therefore, snapshots that are not reachable will be ignored for composition
+ // and input.
+ ReachableByRelativeParent
+ };
+ Reachablilty reachablilty;
static bool isOpaqueFormat(PixelFormat format);
static bool isTransformValid(const ui::Transform& t);
@@ -116,6 +144,10 @@
std::string getIsVisibleReason() const;
bool hasInputInfo() const;
FloatRect sourceBounds() const;
+ Hwc2::IComposerClient::BlendMode getBlendMode(const RequestedLayerState& requested) const;
+
+ void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges,
+ bool forceFullDamage, uint32_t displayRotationFlags);
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 96ff70c..21f0a67 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -17,13 +17,14 @@
// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
-#define LOG_TAG "LayerSnapshotBuilder"
+#define LOG_TAG "SurfaceFlinger"
#include <numeric>
#include <optional>
#include <ftl/small_map.h>
#include <gui/TraceUtils.h>
+#include <ui/DisplayMap.h>
#include <ui/FloatRect.h>
#include "DisplayHardware/HWC2.h"
@@ -31,6 +32,7 @@
#include "LayerLog.h"
#include "LayerSnapshotBuilder.h"
#include "TimeStats/TimeStats.h"
+#include "Tracing/TransactionTracing.h"
namespace android::surfaceflinger::frontend {
@@ -256,19 +258,6 @@
return blendMode;
}
-void updateSurfaceDamage(const RequestedLayerState& requested, bool hasReadyFrame,
- bool forceFullDamage, Region& outSurfaceDamageRegion) {
- if (!hasReadyFrame) {
- outSurfaceDamageRegion.clear();
- return;
- }
- if (forceFullDamage) {
- outSurfaceDamageRegion = Region::INVALID_REGION;
- } else {
- outSurfaceDamageRegion = requested.surfaceDamageRegion;
- }
-}
-
void updateVisibility(LayerSnapshot& snapshot, bool visible) {
snapshot.isVisible = visible;
@@ -286,6 +275,8 @@
const bool visibleForInput =
snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
+ LLOGV(snapshot.sequence, "updating visibility %s %s", visible ? "true" : "false",
+ snapshot.getDebugString().c_str());
}
bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
@@ -328,18 +319,31 @@
void clearChanges(LayerSnapshot& snapshot) {
snapshot.changes.clear();
+ snapshot.clientChanges = 0;
snapshot.contentDirty = false;
snapshot.hasReadyFrame = false;
snapshot.sidebandStreamHasFrame = false;
snapshot.surfaceDamage.clear();
}
+// TODO (b/259407931): Remove.
+uint32_t getPrimaryDisplayRotationFlags(
+ const ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays) {
+ for (auto& [_, display] : displays) {
+ if (display.isPrimary) {
+ return display.rotationFlags;
+ }
+ }
+ return 0;
+}
+
} // namespace
LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() {
LayerSnapshot snapshot;
snapshot.path = LayerHierarchy::TraversalPath::ROOT;
snapshot.changes = ftl::Flags<RequestedLayerState::Changes>();
+ snapshot.clientChanges = 0;
snapshot.isHiddenByPolicyFromParent = false;
snapshot.isHiddenByPolicyFromRelativeParent = false;
snapshot.parentTransform.reset();
@@ -373,43 +377,44 @@
}
bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) {
- if (args.forceUpdate != ForceUpdateFlags::NONE || args.displayChanges) {
- // force update requested, or we have display changes, so skip the fast path
- return false;
- }
+ const bool forceUpdate = args.forceUpdate != ForceUpdateFlags::NONE;
- if (args.layerLifecycleManager.getGlobalChanges().get() == 0) {
+ if (args.layerLifecycleManager.getGlobalChanges().get() == 0 && !forceUpdate &&
+ !args.displayChanges) {
return true;
}
- if (args.layerLifecycleManager.getGlobalChanges() != RequestedLayerState::Changes::Content) {
- // We have changes that require us to walk the hierarchy and update child layers.
- // No fast path for you.
- return false;
- }
-
// There are only content changes which do not require any child layer snapshots to be updated.
ALOGV("%s", __func__);
ATRACE_NAME("FastPath");
- // Collect layers with changes
- ftl::SmallMap<uint32_t, RequestedLayerState*, 10> layersWithChanges;
- for (auto& layer : args.layerLifecycleManager.getLayers()) {
- if (layer->changes.test(RequestedLayerState::Changes::Content)) {
- layersWithChanges.emplace_or_replace(layer->id, layer.get());
+ uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
+ if (forceUpdate || args.displayChanges) {
+ for (auto& snapshot : mSnapshots) {
+ const RequestedLayerState* requested =
+ args.layerLifecycleManager.getLayerFromId(snapshot->path.id);
+ if (!requested) continue;
+ snapshot->merge(*requested, forceUpdate, args.displayChanges, args.forceFullDamage,
+ primaryDisplayRotationFlags);
+ }
+ return false;
+ }
+
+ // Walk through all the updated requested layer states and update the corresponding snapshots.
+ for (const RequestedLayerState* requested : args.layerLifecycleManager.getChangedLayers()) {
+ auto range = mIdToSnapshots.equal_range(requested->id);
+ for (auto it = range.first; it != range.second; it++) {
+ it->second->merge(*requested, forceUpdate, args.displayChanges, args.forceFullDamage,
+ primaryDisplayRotationFlags);
}
}
- // Walk through the snapshots, clearing previous change flags and updating the snapshots
- // if needed.
- for (auto& snapshot : mSnapshots) {
- auto it = layersWithChanges.find(snapshot->path.id);
- if (it != layersWithChanges.end()) {
- ALOGV("%s fast path snapshot changes = %s", __func__,
- mRootSnapshot.changes.string().c_str());
- LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
- updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root);
- }
+ if ((args.layerLifecycleManager.getGlobalChanges().get() &
+ ~(RequestedLayerState::Changes::Content | RequestedLayerState::Changes::Buffer).get()) !=
+ 0) {
+ // We have changes that require us to walk the hierarchy and update child layers.
+ // No fast path for you.
+ return false;
}
return true;
}
@@ -428,20 +433,28 @@
if (args.forceUpdate == ForceUpdateFlags::HIERARCHY) {
mRootSnapshot.changes |=
RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Visibility;
+ mRootSnapshot.clientChanges |= layer_state_t::eReparent;
}
+
+ for (auto& snapshot : mSnapshots) {
+ if (snapshot->reachablilty == LayerSnapshot::Reachablilty::Reachable) {
+ snapshot->reachablilty = LayerSnapshot::Reachablilty::Unreachable;
+ }
+ }
+
LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
if (args.root.getLayer()) {
// The hierarchy can have a root layer when used for screenshots otherwise, it will have
// multiple children.
LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, args.root.getLayer()->id,
LayerHierarchy::Variant::Attached);
- updateSnapshotsInHierarchy(args, args.root, root, mRootSnapshot);
+ updateSnapshotsInHierarchy(args, args.root, root, mRootSnapshot, /*depth=*/0);
} else {
for (auto& [childHierarchy, variant] : args.root.mChildren) {
LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root,
childHierarchy->getLayer()->id,
variant);
- updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot);
+ updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot, /*depth=*/0);
}
}
@@ -467,13 +480,26 @@
auto it = mSnapshots.begin();
while (it < mSnapshots.end()) {
auto& traversalPath = it->get()->path;
- if (!it->get()->unreachable &&
- destroyedLayerIds.find(traversalPath.id) == destroyedLayerIds.end()) {
+ const bool unreachable =
+ it->get()->reachablilty == LayerSnapshot::Reachablilty::Unreachable;
+ const bool isClone = traversalPath.isClone();
+ const bool layerIsDestroyed =
+ destroyedLayerIds.find(traversalPath.id) != destroyedLayerIds.end();
+ const bool destroySnapshot = (unreachable && isClone) || layerIsDestroyed;
+
+ if (!destroySnapshot) {
it++;
continue;
}
- mIdToSnapshot.erase(traversalPath);
+ mPathToSnapshot.erase(traversalPath);
+
+ auto range = mIdToSnapshots.equal_range(traversalPath.id);
+ auto matchingSnapshot =
+ std::find_if(range.first, range.second, [&traversalPath](auto& snapshotWithId) {
+ return snapshotWithId.second->path == traversalPath;
+ });
+ mIdToSnapshots.erase(matchingSnapshot);
mNeedsTouchableRegionCrop.erase(traversalPath);
mSnapshots.back()->globalZ = it->get()->globalZ;
std::iter_swap(it, mSnapshots.end() - 1);
@@ -494,12 +520,24 @@
const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy(
const Args& args, const LayerHierarchy& hierarchy,
- LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot) {
+ LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot,
+ int depth) {
+ if (depth > 50) {
+ TransactionTraceWriter::getInstance().invoke("layer_builder_stack_overflow_",
+ /*overwrite=*/false);
+ LOG_ALWAYS_FATAL("Cycle detected in LayerSnapshotBuilder. See "
+ "builder_stack_overflow_transactions.winscope");
+ }
+
const RequestedLayerState* layer = hierarchy.getLayer();
LayerSnapshot* snapshot = getSnapshot(traversalPath);
const bool newSnapshot = snapshot == nullptr;
+ uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
if (newSnapshot) {
snapshot = createSnapshot(traversalPath, *layer, parentSnapshot);
+ snapshot->merge(*layer, /*forceUpdate=*/true, /*displayChanges=*/true, args.forceFullDamage,
+ primaryDisplayRotationFlags);
+ snapshot->changes |= RequestedLayerState::Changes::Created;
}
scheduler::LayerInfo::FrameRate oldFrameRate = snapshot->frameRate;
if (traversalPath.isRelative()) {
@@ -517,7 +555,8 @@
childHierarchy->getLayer()->id,
variant);
const LayerSnapshot& childSnapshot =
- updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot);
+ updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot,
+ depth + 1);
updateChildState(*snapshot, childSnapshot, args);
}
@@ -536,8 +575,8 @@
}
LayerSnapshot* LayerSnapshotBuilder::getSnapshot(const LayerHierarchy::TraversalPath& id) const {
- auto it = mIdToSnapshot.find(id);
- return it == mIdToSnapshot.end() ? nullptr : it->second;
+ auto it = mPathToSnapshot.find(id);
+ return it == mPathToSnapshot.end() ? nullptr : it->second;
}
LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& path,
@@ -549,7 +588,9 @@
if (path.isClone() && path.variant != LayerHierarchy::Variant::Mirror) {
snapshot->mirrorRootPath = parentSnapshot.mirrorRootPath;
}
- mIdToSnapshot[path] = snapshot;
+ mPathToSnapshot[path] = snapshot;
+
+ mIdToSnapshots.emplace(path.id, snapshot);
return snapshot;
}
@@ -564,20 +605,15 @@
}
mResortSnapshots = false;
- for (auto& snapshot : mSnapshots) {
- snapshot->unreachable = snapshot->path.isClone();
- }
-
size_t globalZ = 0;
args.root.traverseInZOrder(
[this, &globalZ](const LayerHierarchy&,
const LayerHierarchy::TraversalPath& traversalPath) -> bool {
LayerSnapshot* snapshot = getSnapshot(traversalPath);
if (!snapshot) {
- return false;
+ return true;
}
- snapshot->unreachable = false;
if (snapshot->getIsVisible() || snapshot->hasInputInfo()) {
updateVisibility(*snapshot, snapshot->getIsVisible());
size_t oldZ = snapshot->globalZ;
@@ -600,7 +636,7 @@
mSnapshots[globalZ]->globalZ = globalZ;
/* mark unreachable snapshots as explicitly invisible */
updateVisibility(*mSnapshots[globalZ], false);
- if (mSnapshots[globalZ]->unreachable) {
+ if (mSnapshots[globalZ]->reachablilty == LayerSnapshot::Reachablilty::Unreachable) {
hasUnreachableSnapshots = true;
}
globalZ++;
@@ -624,7 +660,9 @@
snapshot.relativeLayerMetadata = parentSnapshot.relativeLayerMetadata;
}
}
- snapshot.isVisible = snapshot.getIsVisible();
+ if (snapshot.reachablilty == LayerSnapshot::Reachablilty::Unreachable) {
+ snapshot.reachablilty = LayerSnapshot::Reachablilty::ReachableByRelativeParent;
+ }
}
void LayerSnapshotBuilder::updateChildState(LayerSnapshot& snapshot,
@@ -665,16 +703,6 @@
snapshot.relativeLayerMetadata.mMap.clear();
}
-// TODO (b/259407931): Remove.
-uint32_t getPrimaryDisplayRotationFlags(const DisplayInfos& displays) {
- for (auto& [_, display] : displays) {
- if (display.isPrimary) {
- return display.rotationFlags;
- }
- }
- return 0;
-}
-
void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& args,
const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot,
@@ -684,82 +712,69 @@
(RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Metadata |
RequestedLayerState::Changes::AffectsChildren |
- RequestedLayerState::Changes::FrameRate);
- snapshot.changes |= parentChanges | requested.changes;
+ RequestedLayerState::Changes::FrameRate | RequestedLayerState::Changes::GameMode);
+ snapshot.changes |= parentChanges;
+ if (args.displayChanges) snapshot.changes |= RequestedLayerState::Changes::Geometry;
+ snapshot.reachablilty = LayerSnapshot::Reachablilty::Reachable;
+ snapshot.clientChanges |= (parentSnapshot.clientChanges & layer_state_t::AFFECTS_CHILDREN);
snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent ||
parentSnapshot.invalidTransform || requested.isHiddenByPolicy() ||
(args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end());
- snapshot.contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
- // TODO(b/238781169) scope down the changes to only buffer updates.
- snapshot.hasReadyFrame = requested.hasReadyFrame();
- snapshot.sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
- updateSurfaceDamage(requested, snapshot.hasReadyFrame, args.forceFullDamage,
- snapshot.surfaceDamage);
- snapshot.outputFilter.layerStack = parentSnapshot.path == LayerHierarchy::TraversalPath::ROOT
- ? requested.layerStack
- : parentSnapshot.outputFilter.layerStack;
- uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL ||
+ snapshot.clientChanges & layer_state_t::eReparent ||
snapshot.changes.any(RequestedLayerState::Changes::Visibility |
RequestedLayerState::Changes::Created);
- // always update the buffer regardless of visibility
- if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES || args.displayChanges) {
- snapshot.acquireFence =
- (requested.externalTexture &&
- requested.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged))
- ? requested.bufferData->acquireFence
- : Fence::NO_FENCE;
- snapshot.buffer =
- requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
- snapshot.bufferSize = requested.getBufferSize(primaryDisplayRotationFlags);
- snapshot.geomBufferSize = snapshot.bufferSize;
- snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
- snapshot.dataspace = requested.dataspace;
- snapshot.externalTexture = requested.externalTexture;
- snapshot.frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0;
- snapshot.geomBufferTransform = requested.bufferTransform;
- snapshot.geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
- snapshot.geomContentCrop = requested.getBufferCrop();
- snapshot.geomUsesSourceCrop = snapshot.hasBufferOrSidebandStream();
- snapshot.hasProtectedContent = requested.externalTexture &&
- requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
- snapshot.isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
- requested.api == NATIVE_WINDOW_API_MEDIA &&
- requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
- snapshot.sidebandStream = requested.sidebandStream;
- snapshot.transparentRegionHint = requested.transparentRegion;
- snapshot.color.rgb = requested.getColor().rgb;
- snapshot.currentHdrSdrRatio = requested.currentHdrSdrRatio;
- snapshot.desiredHdrSdrRatio = requested.desiredHdrSdrRatio;
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eLayerStackChanged) {
+ // If root layer, use the layer stack otherwise get the parent's layer stack.
+ snapshot.outputFilter.layerStack =
+ parentSnapshot.path == LayerHierarchy::TraversalPath::ROOT
+ ? requested.layerStack
+ : parentSnapshot.outputFilter.layerStack;
}
if (snapshot.isHiddenByPolicyFromParent &&
!snapshot.changes.test(RequestedLayerState::Changes::Created)) {
if (forceUpdate ||
- snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
- RequestedLayerState::Changes::Geometry |
+ snapshot.changes.any(RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Input)) {
updateInput(snapshot, requested, parentSnapshot, path, args);
}
return;
}
- if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) {
- // If root layer, use the layer stack otherwise get the parent's layer stack.
+ if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Mirror)) {
+ // Display mirrors are always placed in a VirtualDisplay so we never want to capture layers
+ // marked as skip capture
+ snapshot.handleSkipScreenshotFlag = parentSnapshot.handleSkipScreenshotFlag ||
+ (requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
+ }
+
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eAlphaChanged) {
snapshot.color.a = parentSnapshot.color.a * requested.color.a;
snapshot.alpha = snapshot.color.a;
snapshot.inputInfo.alpha = snapshot.color.a;
+ }
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eFlagsChanged) {
snapshot.isSecure =
parentSnapshot.isSecure || (requested.flags & layer_state_t::eLayerSecure);
- snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
snapshot.outputFilter.toInternalDisplay = parentSnapshot.outputFilter.toInternalDisplay ||
(requested.flags & layer_state_t::eLayerSkipScreenshot);
+ }
+
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eTrustedOverlayChanged) {
+ snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
+ }
+
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eStretchChanged) {
snapshot.stretchEffect = (requested.stretchEffect.hasEffect())
? requested.stretchEffect
: parentSnapshot.stretchEffect;
+ }
+
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eColorTransformChanged) {
if (!parentSnapshot.colorTransformIsIdentity) {
snapshot.colorTransform = parentSnapshot.colorTransform * requested.colorTransform;
snapshot.colorTransformIsIdentity = false;
@@ -767,16 +782,20 @@
snapshot.colorTransform = requested.colorTransform;
snapshot.colorTransformIsIdentity = !requested.hasColorTransform;
}
+ }
+
+ if (forceUpdate || snapshot.changes.test(RequestedLayerState::Changes::GameMode)) {
snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
? requested.gameMode
: parentSnapshot.gameMode;
- // Display mirrors are always placed in a VirtualDisplay so we never want to capture layers
- // marked as skip capture
- snapshot.handleSkipScreenshotFlag = parentSnapshot.handleSkipScreenshotFlag ||
- (requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
+ updateMetadata(snapshot, requested, args);
+ if (args.includeMetadata) {
+ snapshot.layerMetadata = parentSnapshot.layerMetadata;
+ snapshot.layerMetadata.merge(requested.metadata);
+ }
}
- if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren) ||
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eFixedTransformHintChanged ||
args.displayChanges) {
snapshot.fixedTransformHint = requested.fixedTransformHint != ui::Transform::ROT_INVALID
? requested.fixedTransformHint
@@ -792,9 +811,7 @@
}
}
- if (forceUpdate ||
- snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
- RequestedLayerState::Changes::Hierarchy)) {
+ if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::FrameRate)) {
snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
(requested.requestedFrameRate.type ==
scheduler::LayerInfo::FrameRateCompatibility::NoVote))
@@ -802,23 +819,10 @@
: parentSnapshot.frameRate;
}
- if (forceUpdate || requested.what & layer_state_t::eMetadataChanged) {
- updateMetadata(snapshot, requested, args);
- }
-
- if (forceUpdate || requested.changes.get() != 0) {
- snapshot.compositionType = requested.getCompositionType();
- snapshot.dimmingEnabled = requested.dimmingEnabled;
- snapshot.layerOpaqueFlagSet =
- (requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
- snapshot.cachingHint = requested.cachingHint;
- snapshot.frameRateSelectionPriority = requested.frameRateSelectionPriority;
- }
-
- if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content) ||
- snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) {
- snapshot.color.rgb = requested.getColor().rgb;
- snapshot.isColorspaceAgnostic = requested.colorSpaceAgnostic;
+ if (forceUpdate ||
+ snapshot.clientChanges &
+ (layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged |
+ layer_state_t::eAlphaChanged)) {
snapshot.backgroundBlurRadius = args.supportsBlur
? static_cast<int>(parentSnapshot.color.a * (float)requested.backgroundBlurRadius)
: 0;
@@ -826,29 +830,30 @@
for (auto& region : snapshot.blurRegions) {
region.alpha = region.alpha * snapshot.color.a;
}
- snapshot.hdrMetadata = requested.hdrMetadata;
}
- if (forceUpdate ||
- snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
- RequestedLayerState::Changes::Geometry)) {
+ if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Geometry)) {
+ uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
updateLayerBounds(snapshot, requested, parentSnapshot, primaryDisplayRotationFlags);
+ }
+
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged ||
+ snapshot.changes.any(RequestedLayerState::Changes::Geometry)) {
updateRoundedCorner(snapshot, requested, parentSnapshot);
}
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eShadowRadiusChanged ||
+ snapshot.changes.any(RequestedLayerState::Changes::Geometry)) {
+ updateShadows(snapshot, requested, args.globalShadowSettings);
+ }
+
if (forceUpdate ||
- snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
- RequestedLayerState::Changes::Geometry |
+ snapshot.changes.any(RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Input)) {
updateInput(snapshot, requested, parentSnapshot, path, args);
}
// computed snapshot properties
- updateShadows(snapshot, requested, args.globalShadowSettings);
- if (args.includeMetadata) {
- snapshot.layerMetadata = parentSnapshot.layerMetadata;
- snapshot.layerMetadata.merge(requested.metadata);
- }
snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 ||
requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect();
snapshot.contentOpaque = snapshot.isContentOpaque();
@@ -904,10 +909,6 @@
const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot,
uint32_t primaryDisplayRotationFlags) {
- snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
- snapshot.geomCrop = requested.crop;
- snapshot.localTransform = requested.getTransform(primaryDisplayRotationFlags);
- snapshot.localTransformInverse = snapshot.localTransform.inverse();
snapshot.geomLayerTransform = parentSnapshot.geomLayerTransform * snapshot.localTransform;
const bool transformWasInvalid = snapshot.invalidTransform;
snapshot.invalidTransform = !LayerSnapshot::isTransformValid(snapshot.geomLayerTransform);
@@ -964,11 +965,8 @@
}
}
-void LayerSnapshotBuilder::updateShadows(LayerSnapshot& snapshot,
- const RequestedLayerState& requested,
+void LayerSnapshotBuilder::updateShadows(LayerSnapshot& snapshot, const RequestedLayerState&,
const renderengine::ShadowSettings& globalShadowSettings) {
- snapshot.shadowRadius = requested.shadowRadius;
- snapshot.shadowSettings.length = requested.shadowRadius;
if (snapshot.shadowRadius > 0.f) {
snapshot.shadowSettings = globalShadowSettings;
@@ -997,8 +995,8 @@
snapshot.inputInfo = {};
// b/271132344 revisit this and see if we can always use the layers uid/pid
snapshot.inputInfo.name = requested.name;
- snapshot.inputInfo.ownerUid = static_cast<int32_t>(requested.ownerUid);
- snapshot.inputInfo.ownerPid = requested.ownerPid;
+ snapshot.inputInfo.ownerUid = gui::Uid{requested.ownerUid};
+ snapshot.inputInfo.ownerPid = gui::Pid{requested.ownerPid};
}
snapshot.touchCropId = requested.touchCropId;
@@ -1048,10 +1046,11 @@
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT;
}
- auto cropLayerSnapshot = getSnapshot(requested.touchCropId);
- if (cropLayerSnapshot) {
+ if (requested.touchCropId != UNASSIGNED_LAYER_ID || path.isClone()) {
mNeedsTouchableRegionCrop.insert(path);
- } else if (snapshot.inputInfo.replaceTouchableRegionWithCrop) {
+ }
+ auto cropLayerSnapshot = getSnapshot(requested.touchCropId);
+ if (!cropLayerSnapshot && snapshot.inputInfo.replaceTouchableRegionWithCrop) {
FloatRect inputBounds = getInputBounds(snapshot, /*fillParentBounds=*/true).first;
Rect inputBoundsInDisplaySpace =
getInputBoundsInDisplaySpace(snapshot, inputBounds, displayInfo.transform);
@@ -1071,8 +1070,6 @@
// Cloned layers shouldn't handle watch outside since their z order is not determined by
// WM or the client.
snapshot.inputInfo.inputConfig.clear(gui::WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH);
-
- mNeedsTouchableRegionCrop.insert(path);
}
}
@@ -1129,7 +1126,7 @@
RequestedLayerState::Changes::Input;
if (args.forceUpdate != ForceUpdateFlags::ALL &&
- !args.layerLifecycleManager.getGlobalChanges().any(AFFECTS_INPUT)) {
+ !args.layerLifecycleManager.getGlobalChanges().any(AFFECTS_INPUT) && !args.displayChanges) {
return;
}
@@ -1138,6 +1135,8 @@
if (!snapshot) {
continue;
}
+ LLOGV(snapshot->sequence, "updateTouchableRegionCrop=%s",
+ snapshot->getDebugString().c_str());
const std::optional<frontend::DisplayInfo> displayInfoOpt =
args.displays.get(snapshot->outputFilter.layerStack);
static frontend::DisplayInfo sDefaultInfo = {.isSecure = false};
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 3f33ab8..c81a5d2 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -96,7 +96,7 @@
const LayerSnapshot& updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy,
LayerHierarchy::TraversalPath& traversalPath,
- const LayerSnapshot& parentSnapshot);
+ const LayerSnapshot& parentSnapshot, int depth);
void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&,
const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&);
static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot,
@@ -122,7 +122,9 @@
std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
LayerHierarchy::TraversalPathHash>
- mIdToSnapshot;
+ mPathToSnapshot;
+ std::multimap<uint32_t, LayerSnapshot*> mIdToSnapshots;
+
// Track snapshots that needs touchable region crop from other snapshots
std::unordered_set<LayerHierarchy::TraversalPath, LayerHierarchy::TraversalPathHash>
mNeedsTouchableRegionCrop;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 23bb54c..5738262 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
-#define LOG_TAG "RequestedLayerState"
+#define LOG_TAG "SurfaceFlinger"
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -131,12 +132,17 @@
const uint32_t oldFlags = flags;
const half oldAlpha = color.a;
const bool hadBuffer = externalTexture != nullptr;
+ uint64_t oldFramenumber = hadBuffer ? bufferData->frameNumber : 0;
+ const ui::Size oldBufferSize = hadBuffer
+ ? ui::Size(externalTexture->getWidth(), externalTexture->getHeight())
+ : ui::Size();
const bool hadSideStream = sidebandStream != nullptr;
const layer_state_t& clientState = resolvedComposerState.state;
const bool hadBlur = hasBlur();
uint64_t clientChanges = what | layer_state_t::diff(clientState);
layer_state_t::merge(clientState);
what = clientChanges;
+ LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges);
if (clientState.what & layer_state_t::eFlagsChanged) {
if ((oldFlags ^ flags) & layer_state_t::eLayerHidden) {
@@ -147,15 +153,19 @@
changes |= RequestedLayerState::Changes::Geometry;
}
}
+
if (clientState.what & layer_state_t::eBufferChanged) {
externalTexture = resolvedComposerState.externalTexture;
- barrierProducerId = std::max(bufferData->producerId, barrierProducerId);
- barrierFrameNumber = std::max(bufferData->frameNumber, barrierFrameNumber);
- // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
-
const bool hasBuffer = externalTexture != nullptr;
if (hasBuffer || hasBuffer != hadBuffer) {
changes |= RequestedLayerState::Changes::Buffer;
+ const ui::Size newBufferSize = hasBuffer
+ ? ui::Size(externalTexture->getWidth(), externalTexture->getHeight())
+ : ui::Size();
+ if (oldBufferSize != newBufferSize) {
+ changes |= RequestedLayerState::Changes::BufferSize;
+ changes |= RequestedLayerState::Changes::Geometry;
+ }
}
if (hasBuffer != hadBuffer) {
@@ -163,6 +173,28 @@
RequestedLayerState::Changes::VisibleRegion |
RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input;
}
+
+ if (hasBuffer) {
+ const bool frameNumberChanged =
+ bufferData->flags.test(BufferData::BufferDataChange::frameNumberChanged);
+ const uint64_t frameNumber =
+ frameNumberChanged ? bufferData->frameNumber : oldFramenumber + 1;
+ bufferData->frameNumber = frameNumber;
+
+ if ((barrierProducerId > bufferData->producerId) ||
+ ((barrierProducerId == bufferData->producerId) &&
+ (barrierFrameNumber > bufferData->frameNumber))) {
+ ALOGE("Out of order buffers detected for %s producedId=%d frameNumber=%" PRIu64
+ " -> producedId=%d frameNumber=%" PRIu64,
+ getDebugString().c_str(), bufferData->producerId, bufferData->frameNumber,
+ bufferData->producerId, frameNumber);
+ TransactionTraceWriter::getInstance().invoke("out_of_order_buffers_",
+ /*overwrite=*/false);
+ }
+
+ barrierProducerId = std::max(bufferData->producerId, barrierProducerId);
+ barrierFrameNumber = std::max(bufferData->frameNumber, barrierFrameNumber);
+ }
}
if (clientState.what & layer_state_t::eSidebandStreamChanged) {
@@ -261,7 +293,7 @@
// child layers.
if (static_cast<int32_t>(gameMode) != requestedGameMode) {
gameMode = static_cast<gui::GameMode>(requestedGameMode);
- changes |= RequestedLayerState::Changes::AffectsChildren;
+ changes |= RequestedLayerState::Changes::GameMode;
}
}
}
@@ -352,7 +384,7 @@
return (flags & layer_state_t::eLayerHidden) == layer_state_t::eLayerHidden;
};
half4 RequestedLayerState::getColor() const {
- if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+ if (sidebandStream || externalTexture) {
return {0._hf, 0._hf, 0._hf, color.a};
}
return color;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 0ef50bc..02e3bac 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -54,6 +54,8 @@
Buffer = 1u << 15,
SidebandStream = 1u << 16,
Animation = 1u << 17,
+ BufferSize = 1u << 18,
+ GameMode = 1u << 19,
};
static Rect reduce(const Rect& win, const Region& exclude);
RequestedLayerState(const LayerCreationArgs&);
@@ -91,10 +93,10 @@
const uint32_t textureName;
// The owner of the layer. If created from a non system process, it will be the calling uid.
// If created from a system process, the value can be passed in.
- const uid_t ownerUid;
+ const gui::Uid ownerUid;
// The owner pid of the layer. If created from a non system process, it will be the calling pid.
// If created from a system process, the value can be passed in.
- const pid_t ownerPid;
+ const gui::Pid ownerPid;
bool dataspaceRequested;
bool hasColorTransform;
bool premultipliedAlpha{true};
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 9cbe0bb..fa8eb3c 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -16,7 +16,7 @@
// #define LOG_NDEBUG 0
#undef LOG_TAG
-#define LOG_TAG "TransactionHandler"
+#define LOG_TAG "SurfaceFlinger"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <cutils/trace.h>
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index cf1b018..5a010e8 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1349,6 +1349,8 @@
mDrawingState.bufferSurfaceFrameTX =
createSurfaceFrameForBuffer(info, postTime, mTransactionName);
}
+
+ setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName);
}
void Layer::setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
@@ -1380,11 +1382,13 @@
it->second = createSurfaceFrameForTransaction(info, postTime);
}
}
+
+ setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName);
}
void Layer::addSurfaceFrameDroppedForBuffer(
- std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
- surfaceFrame->setDropTime(systemTime());
+ std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame, nsecs_t dropTime) {
+ surfaceFrame->setDropTime(dropTime);
surfaceFrame->setPresentState(PresentState::Dropped);
mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
}
@@ -1434,6 +1438,32 @@
return surfaceFrame;
}
+void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime,
+ std::string debugName) {
+ if (info.skippedFrameVsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
+ return;
+ }
+
+ FrameTimelineInfo skippedFrameTimelineInfo = info;
+ skippedFrameTimelineInfo.vsyncId = info.skippedFrameVsyncId;
+
+ auto surfaceFrame =
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(skippedFrameTimelineInfo,
+ mOwnerPid, mOwnerUid,
+ getSequence(), mName, debugName,
+ /*isBuffer*/ false, getGameMode());
+ surfaceFrame->setActualStartTime(skippedFrameTimelineInfo.skippedFrameStartTimeNanos);
+ // For Transactions, the post time is considered to be both queue and acquire fence time.
+ surfaceFrame->setActualQueueTime(postTime);
+ surfaceFrame->setAcquireFenceTime(postTime);
+ const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
+ if (fps) {
+ surfaceFrame->setRenderRate(*fps);
+ }
+ onSurfaceFrameCreated(surfaceFrame);
+ addSurfaceFrameDroppedForBuffer(surfaceFrame, postTime);
+}
+
bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate) {
if (mDrawingState.frameRateForLayerTree == frameRate) {
return false;
@@ -2422,8 +2452,8 @@
WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
if (!hasInputInfo()) {
mDrawingState.inputInfo.name = getName();
- mDrawingState.inputInfo.ownerUid = mOwnerUid;
- mDrawingState.inputInfo.ownerPid = mOwnerPid;
+ mDrawingState.inputInfo.ownerUid = gui::Uid{mOwnerUid};
+ mDrawingState.inputInfo.ownerPid = gui::Pid{mOwnerPid};
mDrawingState.inputInfo.inputConfig |= WindowInfo::InputConfig::NO_INPUT_CHANNEL;
mDrawingState.inputInfo.displayId = getLayerStack().id;
}
@@ -3067,7 +3097,7 @@
decrementPendingBufferCount();
if (mDrawingState.bufferSurfaceFrameTX != nullptr &&
mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) {
- addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX);
+ addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX, systemTime());
mDrawingState.bufferSurfaceFrameTX.reset();
}
} else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) {
@@ -3095,6 +3125,16 @@
return true;
}
+ if ((mDrawingState.producerId > bufferData.producerId) ||
+ ((mDrawingState.producerId == bufferData.producerId) &&
+ (mDrawingState.frameNumber > frameNumber))) {
+ ALOGE("Out of order buffers detected for %s producedId=%d frameNumber=%" PRIu64
+ " -> producedId=%d frameNumber=%" PRIu64,
+ getDebugName(), mDrawingState.producerId, mDrawingState.frameNumber,
+ bufferData.producerId, frameNumber);
+ TransactionTraceWriter::getInstance().invoke("out_of_order_buffers_", /*overwrite=*/false);
+ }
+
mDrawingState.producerId = bufferData.producerId;
mDrawingState.barrierProducerId =
std::max(mDrawingState.producerId, mDrawingState.barrierProducerId);
@@ -3102,7 +3142,6 @@
mDrawingState.barrierFrameNumber =
std::max(mDrawingState.frameNumber, mDrawingState.barrierFrameNumber);
- // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
mDrawingState.buffer = std::move(buffer);
mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f34fdd9..2fbbbdc 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -785,8 +785,8 @@
void setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
nsecs_t postTime);
- void addSurfaceFrameDroppedForBuffer(
- std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
+ void addSurfaceFrameDroppedForBuffer(std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame,
+ nsecs_t dropTime);
void addSurfaceFramePresentedForBuffer(
std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame, nsecs_t acquireFenceTime,
nsecs_t currentLatchTime);
@@ -795,6 +795,8 @@
const FrameTimelineInfo& info, nsecs_t postTime);
std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForBuffer(
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName);
+ void setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime,
+ std::string debugName);
bool setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
TrustedPresentationListener const& listener);
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index e61916c..1c7581b 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -178,6 +178,7 @@
InputWindowInfoProto* proto = getInputWindowInfoProto();
proto->set_layout_params_flags(inputInfo.layoutParamsFlags.get());
+ proto->set_input_config(inputInfo.inputConfig.get());
using U = std::underlying_type_t<WindowInfo::Type>;
// TODO(b/129481165): This static assert can be safely removed once conversion warnings
// are re-enabled.
@@ -427,7 +428,7 @@
layerInfo->set_is_relative_of(requestedState.isRelativeOf);
- layerInfo->set_owner_uid(requestedState.ownerUid);
+ layerInfo->set_owner_uid(requestedState.ownerUid.val());
if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) {
LayerProtoHelper::writeToProto(snapshot.inputInfo, {},
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index d5d8688..6d2586a 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -40,6 +40,7 @@
name: "libscheduler",
defaults: ["libscheduler_defaults"],
srcs: [
+ "src/FrameTargeter.cpp",
"src/PresentLatencyTracker.cpp",
"src/Timer.cpp",
],
@@ -52,6 +53,7 @@
test_suites: ["device-tests"],
defaults: ["libscheduler_defaults"],
srcs: [
+ "tests/FrameTargeterTest.cpp",
"tests/PresentLatencyTrackerTest.cpp",
"tests/TimerTest.cpp",
],
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 918d401..41639b6 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -171,14 +171,21 @@
void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
TimePoint expectedVsyncTime) {
- const TimePoint frameTime = SchedulerClock::now();
+ mPacesetterFrameTargeter.beginFrame({.frameBeginTime = SchedulerClock::now(),
+ .vsyncId = vsyncId,
+ .expectedVsyncTime = expectedVsyncTime,
+ .sfWorkDuration =
+ mVsyncModulator->getVsyncConfig().sfWorkDuration},
+ *getVsyncSchedule());
- if (!compositor.commit(frameTime, vsyncId, expectedVsyncTime)) {
+ if (!compositor.commit(mPacesetterFrameTargeter.target())) {
return;
}
- compositor.composite(frameTime, vsyncId);
+ const auto compositeResult = compositor.composite(mPacesetterFrameTargeter);
compositor.sample();
+
+ mPacesetterFrameTargeter.endFrame(compositeResult);
}
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
@@ -188,23 +195,23 @@
.getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
}
-bool Scheduler::isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const {
+bool Scheduler::isVsyncValid(TimePoint expectedVsyncTime, uid_t uid) const {
const auto frameRate = getFrameRateOverride(uid);
if (!frameRate.has_value()) {
return true;
}
ATRACE_FORMAT("%s uid: %d frameRate: %s", __func__, uid, to_string(*frameRate).c_str());
- return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTimestamp.ns(), *frameRate);
+ return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), *frameRate);
}
-bool Scheduler::isVsyncInPhase(TimePoint timePoint, const Fps frameRate) const {
- return getVsyncSchedule()->getTracker().isVSyncInPhase(timePoint.ns(), frameRate);
+bool Scheduler::isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const {
+ return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), frameRate);
}
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
- return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
- return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid);
+ return [this](nsecs_t expectedVsyncTime, uid_t uid) {
+ return !isVsyncValid(TimePoint::fromNs(expectedVsyncTime), uid);
};
}
@@ -716,6 +723,8 @@
mFrameRateOverrideMappings.dump(dumper);
dumper.eol();
+
+ mPacesetterFrameTargeter.dump(dumper);
}
void Scheduler::dumpVsync(std::string& out) const {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a1354fa..17e9cea 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -35,6 +35,7 @@
#include <ftl/fake_guard.h>
#include <ftl/optional.h>
#include <scheduler/Features.h>
+#include <scheduler/FrameTargeter.h>
#include <scheduler/Time.h>
#include <scheduler/VsyncConfig.h>
#include <ui/DisplayId.h>
@@ -249,9 +250,11 @@
return std::const_pointer_cast<VsyncSchedule>(std::as_const(*this).getVsyncSchedule(idOpt));
}
+ const FrameTarget& pacesetterFrameTarget() { return mPacesetterFrameTargeter.target(); }
+
// Returns true if a given vsync timestamp is considered valid vsync
// for a given uid
- bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const;
+ bool isVsyncValid(TimePoint expectedVsyncTime, uid_t uid) const;
bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const;
@@ -446,6 +449,8 @@
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/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 5691792..ff3f29d 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -17,7 +17,6 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <ftl/fake_guard.h>
-#include <gui/TraceUtils.h>
#include <scheduler/Fps.h>
#include <scheduler/Timer.h>
@@ -145,14 +144,6 @@
}
bool VsyncSchedule::addResyncSample(TimePoint timestamp, ftl::Optional<Period> hwcVsyncPeriod) {
- ATRACE_CALL();
-
- if (mClearTimestampsOnNextSample) {
- ATRACE_FORMAT("clearing sample after HW vsync enabled", __func__);
- getTracker().resetModel();
- mClearTimestampsOnNextSample = false;
- }
-
bool needsHwVsync = false;
bool periodFlushed = false;
{
@@ -179,7 +170,7 @@
void VsyncSchedule::enableHardwareVsyncLocked() {
if (mHwVsyncState == HwVsyncState::Disabled) {
- mClearTimestampsOnNextSample = true;
+ getTracker().resetModel();
mRequestHardwareVsync(mId, true);
mHwVsyncState = HwVsyncState::Enabled;
}
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 556ef80..0757b57 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -20,14 +20,17 @@
#include <memory>
#include <string>
-#include <ThreadContext.h>
#include <android-base/thread_annotations.h>
#include <ftl/enum.h>
#include <ftl/optional.h>
-#include <scheduler/Features.h>
-#include <scheduler/Time.h>
#include <ui/DisplayId.h>
+#include <scheduler/Features.h>
+#include <scheduler/IVsyncSource.h>
+#include <scheduler/Time.h>
+
+#include "ThreadContext.h"
+
namespace android {
class EventThreadTest;
class VsyncScheduleTest;
@@ -48,15 +51,16 @@
using VsyncTracker = VSyncTracker;
// Schedule that synchronizes to hardware VSYNC of a physical display.
-class VsyncSchedule {
+class VsyncSchedule final : public IVsyncSource {
public:
using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>;
VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync);
~VsyncSchedule();
- Period period() const;
- TimePoint vsyncDeadlineAfter(TimePoint) const;
+ // IVsyncSource overrides:
+ Period period() const override;
+ TimePoint vsyncDeadlineAfter(TimePoint) const override;
// Inform the schedule that the period is changing and the schedule needs to recalibrate
// itself. The schedule will end the period transition internally. This will
@@ -146,11 +150,6 @@
// device is off.
HwVsyncState mPendingHwVsyncState GUARDED_BY(kMainThreadContext) = HwVsyncState::Disabled;
- // Whether to reset the timestamps stored in the vsync model on the next hw vsync sample. This
- // is to avoid clearing the model when hw vsync is enabled, in order to be consistent with the
- // stale timestamps. Instead, clear the model on the first hw vsync callback.
- bool mClearTimestampsOnNextSample = false;
-
class PredictedVsyncTracer;
using TracerPtr = std::unique_ptr<PredictedVsyncTracer>;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Features.h b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
index b3a6a60..200407d 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Features.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
@@ -23,10 +23,11 @@
namespace android::scheduler {
enum class Feature : std::uint8_t {
- kPresentFences = 0b1,
- kKernelIdleTimer = 0b10,
- kContentDetection = 0b100,
- kTracePredictedVsync = 0b1000,
+ kPresentFences = 1 << 0,
+ kKernelIdleTimer = 1 << 1,
+ kContentDetection = 1 << 2,
+ kTracePredictedVsync = 1 << 3,
+ kBackpressureGpuComposition = 1 << 4,
};
using FeatureFlags = ftl::Flags<Feature>;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
new file mode 100644
index 0000000..85f2e64
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+#include <atomic>
+#include <memory>
+
+#include <ui/Fence.h>
+#include <ui/FenceTime.h>
+
+#include <scheduler/Time.h>
+#include <scheduler/VsyncId.h>
+#include <scheduler/interface/CompositeResult.h>
+
+// TODO(b/185536303): Pull to FTL.
+#include "../../../TracedOrdinal.h"
+#include "../../../Utils/Dumper.h"
+
+namespace android::scheduler {
+
+struct IVsyncSource;
+
+// Read-only interface to the metrics computed by FrameTargeter for the latest frame.
+class FrameTarget {
+public:
+ VsyncId vsyncId() const { return mVsyncId; }
+
+ // The time when the frame actually began, as opposed to when it had been scheduled to begin.
+ TimePoint frameBeginTime() const { return mFrameBeginTime; }
+
+ // Relative to when the frame actually began, as opposed to when it had been scheduled to begin.
+ Duration expectedFrameDuration() const { return mExpectedPresentTime - mFrameBeginTime; }
+
+ TimePoint expectedPresentTime() const { return mExpectedPresentTime; }
+
+ // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details.
+ TimePoint pastVsyncTime(Period vsyncPeriod) const;
+
+ // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead.
+ TimePoint previousFrameVsyncTime(Period vsyncPeriod) const {
+ return mExpectedPresentTime - vsyncPeriod;
+ }
+
+ // The present fence for the frame that had targeted the most recent VSYNC before this frame.
+ // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the
+ // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
+ // `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been
+ // signaled by now (unless that frame missed).
+ const FenceTimePtr& presentFenceForPastVsync(Period vsyncPeriod) const;
+
+ // Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead.
+ const FenceTimePtr& presentFenceForPreviousFrame() const {
+ return mPresentFences.front().fenceTime;
+ }
+
+ bool wouldPresentEarly(Period vsyncPeriod) const;
+
+ bool isFramePending() const { return mFramePending; }
+ bool didMissFrame() const { return mFrameMissed; }
+ bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
+
+protected:
+ ~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};
+
+ struct FenceWithFenceTime {
+ sp<Fence> fence = Fence::NO_FENCE;
+ FenceTimePtr fenceTime = FenceTime::NO_FENCE;
+ };
+ std::array<FenceWithFenceTime, 2> mPresentFences;
+
+private:
+ template <int N>
+ inline bool targetsVsyncsAhead(Period vsyncPeriod) const {
+ static_assert(N > 1);
+ return expectedFrameDuration() > (N - 1) * vsyncPeriod;
+ }
+};
+
+// 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) {}
+
+ const FrameTarget& target() const { return *this; }
+
+ struct BeginFrameArgs {
+ TimePoint frameBeginTime;
+ VsyncId vsyncId;
+ TimePoint expectedVsyncTime;
+ Duration sfWorkDuration;
+ };
+
+ void beginFrame(const BeginFrameArgs&, const IVsyncSource&);
+
+ // TODO(b/241285191): Merge with FrameTargeter::endFrame.
+ FenceTimePtr setPresentFence(sp<Fence>);
+
+ void endFrame(const CompositeResult&);
+
+ void dump(utils::Dumper&) const;
+
+private:
+ friend class FrameTargeterTest;
+
+ // For tests.
+ using IsFencePendingFuncPtr = bool (*)(const FenceTimePtr&, int graceTimeMs);
+ void beginFrame(const BeginFrameArgs&, const IVsyncSource&, IsFencePendingFuncPtr);
+ FenceTimePtr setPresentFence(sp<Fence>, FenceTimePtr);
+
+ static bool isFencePending(const FenceTimePtr&, int graceTimeMs);
+
+ const bool mBackpressureGpuComposition;
+
+ TimePoint mScheduledPresentTime;
+ CompositionCoverageFlags mCompositionCoverage;
+
+ std::atomic_uint mFrameMissedCount = 0;
+ std::atomic_uint mHwcFrameMissedCount = 0;
+ std::atomic_uint mGpuFrameMissedCount = 0;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
new file mode 100644
index 0000000..bb2de75
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <scheduler/Time.h>
+
+namespace android::scheduler {
+
+struct IVsyncSource {
+ virtual Period period() const = 0;
+ virtual TimePoint vsyncDeadlineAfter(TimePoint) const = 0;
+
+protected:
+ ~IVsyncSource() = default;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
new file mode 100644
index 0000000..f795f1f
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <scheduler/interface/CompositionCoverage.h>
+
+namespace android {
+
+struct CompositeResult {
+ CompositionCoverageFlags compositionCoverage;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
index cc41925..2696076 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
@@ -18,8 +18,15 @@
#include <scheduler/Time.h>
#include <scheduler/VsyncId.h>
+#include <scheduler/interface/CompositeResult.h>
namespace android {
+namespace scheduler {
+
+class FrameTarget;
+class FrameTargeter;
+
+} // namespace scheduler
struct ICompositor {
// Configures physical displays, processing hotplug and/or mode setting via the Composer HAL.
@@ -27,11 +34,11 @@
// Commits transactions for layers and displays. Returns whether any state has been invalidated,
// i.e. whether a frame should be composited for each display.
- virtual bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) = 0;
+ virtual bool commit(const scheduler::FrameTarget&) = 0;
// Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
// via RenderEngine and the Composer HAL, respectively.
- virtual void composite(TimePoint frameTime, VsyncId) = 0;
+ virtual CompositeResult composite(scheduler::FrameTargeter&) = 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
new file mode 100644
index 0000000..7138afd
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.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 <gui/TraceUtils.h>
+
+#include <scheduler/FrameTargeter.h>
+#include <scheduler/IVsyncSource.h>
+
+namespace android::scheduler {
+
+TimePoint FrameTarget::pastVsyncTime(Period vsyncPeriod) const {
+ // TODO(b/267315508): Generalize to N VSYNCs.
+ const int shift = static_cast<int>(targetsVsyncsAhead<2>(vsyncPeriod));
+ return mExpectedPresentTime - Period::fromNs(vsyncPeriod.ns() << shift);
+}
+
+const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period vsyncPeriod) const {
+ // TODO(b/267315508): Generalize to N VSYNCs.
+ const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(vsyncPeriod));
+ return mPresentFences[i].fenceTime;
+}
+
+bool FrameTarget::wouldPresentEarly(Period vsyncPeriod) const {
+ // TODO(b/241285475): Since this is called during `composite`, the calls to `targetsVsyncsAhead`
+ // should use `TimePoint::now()` in case of delays since `mFrameBeginTime`.
+
+ // TODO(b/267315508): Generalize to N VSYNCs.
+ if (targetsVsyncsAhead<3>(vsyncPeriod)) {
+ return true;
+ }
+
+ const auto fence = presentFenceForPastVsync(vsyncPeriod);
+ return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+}
+
+void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource) {
+ return beginFrame(args, vsyncSource, &FrameTargeter::isFencePending);
+}
+
+void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource,
+ IsFencePendingFuncPtr isFencePendingFuncPtr) {
+ mVsyncId = args.vsyncId;
+ mFrameBeginTime = args.frameBeginTime;
+
+ // The `expectedVsyncTime`, which was predicted when this frame was scheduled, is normally in
+ // the future relative to `frameBeginTime`, but may not be for delayed frames. Adjust
+ // `mExpectedPresentTime` accordingly, but not `mScheduledPresentTime`.
+ const TimePoint lastScheduledPresentTime = mScheduledPresentTime;
+ mScheduledPresentTime = args.expectedVsyncTime;
+
+ const Period vsyncPeriod = vsyncSource.period();
+
+ // Calculate the expected present time once and use the cached value throughout this frame to
+ // make sure all layers are seeing this same value.
+ if (args.expectedVsyncTime >= args.frameBeginTime) {
+ mExpectedPresentTime = args.expectedVsyncTime;
+ } else {
+ mExpectedPresentTime = vsyncSource.vsyncDeadlineAfter(args.frameBeginTime);
+ if (args.sfWorkDuration > vsyncPeriod) {
+ // Inflate the expected present time if we're targeting the next VSYNC.
+ mExpectedPresentTime += vsyncPeriod;
+ }
+ }
+
+ ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId),
+ ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
+ mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
+
+ const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(vsyncPeriod);
+
+ // In cases where the present fence is about to fire, give it a small grace period instead of
+ // giving up on the frame.
+ //
+ // TODO(b/280667110): The grace period should depend on `sfWorkDuration` and `vsyncPeriod` being
+ // approximately equal, not whether backpressure propagation is enabled.
+ const int graceTimeForPresentFenceMs = static_cast<int>(
+ mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu));
+
+ // Pending frames may trigger backpressure propagation.
+ const auto& isFencePending = *isFencePendingFuncPtr;
+ mFramePending = pastPresentFence != FenceTime::NO_FENCE &&
+ isFencePending(pastPresentFence, graceTimeForPresentFenceMs);
+
+ // A frame is missed if the prior frame is still pending. If no longer pending, then we still
+ // count the frame as missed if the predicted present time was further in the past than when the
+ // fence actually fired. Add some slop to correct for drift. This should generally be smaller
+ // than a typical frame duration, but should not be so small that it reports reasonable drift as
+ // a missed frame.
+ mFrameMissed = mFramePending || [&] {
+ const nsecs_t pastPresentTime = pastPresentFence->getSignalTime();
+ if (pastPresentTime < 0) return false;
+ const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
+ return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
+ }();
+
+ mHwcFrameMissed = mFrameMissed && mCompositionCoverage.test(CompositionCoverage::Hwc);
+ mGpuFrameMissed = mFrameMissed && mCompositionCoverage.test(CompositionCoverage::Gpu);
+
+ if (mFrameMissed) mFrameMissedCount++;
+ if (mHwcFrameMissed) mHwcFrameMissedCount++;
+ if (mGpuFrameMissed) mGpuFrameMissedCount++;
+}
+
+void FrameTargeter::endFrame(const CompositeResult& result) {
+ mCompositionCoverage = result.compositionCoverage;
+}
+
+FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence) {
+ auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
+ return setPresentFence(std::move(presentFence), std::move(presentFenceTime));
+}
+
+FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime) {
+ mPresentFences[1] = mPresentFences[0];
+ mPresentFences[0] = {std::move(presentFence), presentFenceTime};
+ return presentFenceTime;
+}
+
+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));
+ dumper.dump({}, "GPU missed frame count: " + std::to_string(mGpuFrameMissedCount));
+}
+
+bool FrameTargeter::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
+ ATRACE_CALL();
+ const status_t status = fence->wait(graceTimeMs);
+
+ // This is the same as Fence::Status::Unsignaled, but it saves a call to getStatus,
+ // which calls wait(0) again internally.
+ return status == -ETIME;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
new file mode 100644
index 0000000..908f214
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -0,0 +1,301 @@
+/*
+ * 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 <ftl/optional.h>
+#include <gtest/gtest.h>
+
+#include <scheduler/Fps.h>
+#include <scheduler/FrameTargeter.h>
+#include <scheduler/IVsyncSource.h>
+
+using namespace std::chrono_literals;
+
+namespace android::scheduler {
+namespace {
+
+struct VsyncSource final : IVsyncSource {
+ VsyncSource(Period period, TimePoint deadline) : vsyncPeriod(period), vsyncDeadline(deadline) {}
+
+ const Period vsyncPeriod;
+ const TimePoint vsyncDeadline;
+
+ Period period() const override { return vsyncPeriod; }
+ TimePoint vsyncDeadlineAfter(TimePoint) const override { return vsyncDeadline; }
+};
+
+} // namespace
+
+class FrameTargeterTest : public testing::Test {
+public:
+ const auto& target() const { return mTargeter.target(); }
+
+ struct Frame {
+ Frame(FrameTargeterTest* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime,
+ Duration frameDuration, Fps refreshRate,
+ FrameTargeter::IsFencePendingFuncPtr isFencePendingFuncPtr = Frame::fenceSignaled,
+ const ftl::Optional<VsyncSource>& vsyncSourceOpt = std::nullopt)
+ : testPtr(testPtr), frameBeginTime(frameBeginTime), period(refreshRate.getPeriod()) {
+ const FrameTargeter::BeginFrameArgs args{.frameBeginTime = frameBeginTime,
+ .vsyncId = vsyncId,
+ .expectedVsyncTime =
+ frameBeginTime + frameDuration,
+ .sfWorkDuration = 10ms};
+
+ testPtr->mTargeter.beginFrame(args,
+ vsyncSourceOpt
+ .or_else([&] {
+ return std::make_optional(
+ VsyncSource(period,
+ args.expectedVsyncTime));
+ })
+ .value(),
+ isFencePendingFuncPtr);
+ }
+
+ FenceTimePtr end(CompositionCoverage coverage = CompositionCoverage::Hwc) {
+ if (ended) return nullptr;
+ ended = true;
+
+ auto [fence, fenceTime] = testPtr->mFenceMap.makePendingFenceForTest();
+ testPtr->mTargeter.setPresentFence(std::move(fence), fenceTime);
+
+ testPtr->mTargeter.endFrame({.compositionCoverage = coverage});
+ return fenceTime;
+ }
+
+ ~Frame() {
+ end();
+ frameBeginTime += period;
+ }
+
+ static bool fencePending(const FenceTimePtr&, int) { return true; }
+ static bool fenceSignaled(const FenceTimePtr&, int) { return false; }
+
+ FrameTargeterTest* const testPtr;
+
+ TimePoint& frameBeginTime;
+ const Period period;
+
+ bool ended = false;
+ };
+
+private:
+ FenceToFenceTimeMap mFenceMap;
+
+ static constexpr bool kBackpressureGpuComposition = true;
+ FrameTargeter mTargeter{kBackpressureGpuComposition};
+};
+
+TEST_F(FrameTargeterTest, targetsFrames) {
+ VsyncId vsyncId{42};
+ {
+ TimePoint frameBeginTime(989ms);
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz);
+
+ EXPECT_EQ(target().vsyncId(), VsyncId{42});
+ EXPECT_EQ(target().frameBeginTime(), TimePoint(989ms));
+ EXPECT_EQ(target().expectedPresentTime(), TimePoint(999ms));
+ EXPECT_EQ(target().expectedFrameDuration(), 10ms);
+ }
+ {
+ TimePoint frameBeginTime(1100ms);
+ const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz);
+
+ EXPECT_EQ(target().vsyncId(), VsyncId{43});
+ EXPECT_EQ(target().frameBeginTime(), TimePoint(1100ms));
+ EXPECT_EQ(target().expectedPresentTime(), TimePoint(1111ms));
+ EXPECT_EQ(target().expectedFrameDuration(), 11ms);
+ }
+}
+
+TEST_F(FrameTargeterTest, inflatesExpectedPresentTime) {
+ // Negative such that `expectedVsyncTime` is in the past.
+ constexpr Duration kFrameDuration = -3ms;
+ TimePoint frameBeginTime(777ms);
+
+ constexpr Fps kRefreshRate = 120_Hz;
+ const VsyncSource vsyncSource(kRefreshRate.getPeriod(), frameBeginTime + 5ms);
+ const Frame frame(this, VsyncId{123}, frameBeginTime, kFrameDuration, kRefreshRate,
+ Frame::fenceSignaled, vsyncSource);
+
+ EXPECT_EQ(target().expectedPresentTime(), vsyncSource.vsyncDeadline + vsyncSource.vsyncPeriod);
+}
+
+TEST_F(FrameTargeterTest, recallsPastVsync) {
+ VsyncId vsyncId{111};
+ TimePoint frameBeginTime(1000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 13ms;
+
+ for (int n = 5; n-- > 0;) {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate);
+ const auto fence = frame.end();
+
+ EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod);
+ EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), fence);
+ }
+}
+
+TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) {
+ VsyncId vsyncId{222};
+ TimePoint frameBeginTime(2000ms);
+ constexpr Fps kRefreshRate = 120_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 10ms;
+
+ FenceTimePtr previousFence = FenceTime::NO_FENCE;
+
+ for (int n = 5; n-- > 0;) {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate);
+ const auto fence = frame.end();
+
+ EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
+ EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence);
+
+ previousFence = fence;
+ }
+}
+
+TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
+ constexpr Period kPeriod = (60_Hz).getPeriod();
+ EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE);
+ EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+}
+
+TEST_F(FrameTargeterTest, detectsEarlyPresent) {
+ VsyncId vsyncId{333};
+ TimePoint frameBeginTime(3000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ // The target is not early while past present fences are pending.
+ for (int n = 3; n-- > 0;) {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ }
+
+ // The target is early if the past present fence was signaled.
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+
+ EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+}
+
+TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) {
+ VsyncId vsyncId{444};
+ TimePoint frameBeginTime(4000ms);
+ constexpr Fps kRefreshRate = 120_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ // The target is not early while past present fences are pending.
+ for (int n = 3; n-- > 0;) {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ }
+
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+
+ // The target is two VSYNCs ahead, so the past present fence is still pending.
+ EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+
+ { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); }
+
+ // The target is early if the past present fence was signaled.
+ EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+}
+
+TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) {
+ TimePoint frameBeginTime(5000ms);
+ constexpr Fps kRefreshRate = 144_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate);
+
+ // The target is more than two VSYNCs ahead, but present fences are not tracked that far back.
+ EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+}
+
+TEST_F(FrameTargeterTest, detectsMissedFrames) {
+ VsyncId vsyncId{555};
+ TimePoint frameBeginTime(5000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ EXPECT_FALSE(target().isFramePending());
+ EXPECT_FALSE(target().didMissFrame());
+ EXPECT_FALSE(target().didMissHwcFrame());
+
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().isFramePending());
+
+ // The frame did not miss if the past present fence is invalid.
+ EXPECT_FALSE(target().didMissFrame());
+ EXPECT_FALSE(target().didMissHwcFrame());
+ }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending);
+ EXPECT_TRUE(target().isFramePending());
+
+ // The frame missed if the past present fence is pending.
+ EXPECT_TRUE(target().didMissFrame());
+ EXPECT_TRUE(target().didMissHwcFrame());
+
+ frame.end(CompositionCoverage::Gpu);
+ }
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending);
+ EXPECT_TRUE(target().isFramePending());
+
+ // The GPU frame missed if the past present fence is pending.
+ EXPECT_TRUE(target().didMissFrame());
+ EXPECT_FALSE(target().didMissHwcFrame());
+ }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().isFramePending());
+
+ const auto fence = frame.end();
+ const auto expectedPresentTime = target().expectedPresentTime();
+ fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2 + 1);
+ }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().isFramePending());
+
+ const auto fence = frame.end();
+ const auto expectedPresentTime = target().expectedPresentTime();
+ fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2);
+
+ // The frame missed if the past present fence was signaled but not within slop.
+ EXPECT_TRUE(target().didMissFrame());
+ EXPECT_TRUE(target().didMissHwcFrame());
+ }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ EXPECT_FALSE(target().isFramePending());
+
+ // The frame did not miss if the past present fence was signaled within slop.
+ EXPECT_FALSE(target().didMissFrame());
+ EXPECT_FALSE(target().didMissHwcFrame());
+ }
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp
index 8952ca9..df2ea83 100644
--- a/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp
@@ -23,16 +23,6 @@
#include <ui/FenceTime.h>
namespace android::scheduler {
-namespace {
-
-using FencePair = std::pair<sp<Fence>, std::shared_ptr<FenceTime>>;
-
-FencePair makePendingFence(FenceToFenceTimeMap& fenceMap) {
- const auto fence = sp<Fence>::make();
- return {fence, fenceMap.createFenceTimeForTest(fence)};
-}
-
-} // namespace
TEST(PresentLatencyTrackerTest, skipsInvalidFences) {
PresentLatencyTracker tracker;
@@ -43,7 +33,7 @@
EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero());
FenceToFenceTimeMap fenceMap;
- const auto [fence, fenceTime] = makePendingFence(fenceMap);
+ const auto [fence, fenceTime] = fenceMap.makePendingFenceForTest();
EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fenceTime), Duration::zero());
fenceTime->signalForTest(9999);
@@ -56,8 +46,9 @@
PresentLatencyTracker tracker;
FenceToFenceTimeMap fenceMap;
- std::array<FencePair, PresentLatencyTracker::kMaxPendingFrames> fences;
- std::generate(fences.begin(), fences.end(), [&fenceMap] { return makePendingFence(fenceMap); });
+ std::array<FenceToFenceTimeMap::FencePair, PresentLatencyTracker::kMaxPendingFrames> fences;
+ std::generate(fences.begin(), fences.end(),
+ [&fenceMap] { return fenceMap.makePendingFenceForTest(); });
// The present latency is 0 if all fences are pending.
const TimePoint kCompositeTime = TimePoint::fromNs(1234);
@@ -71,7 +62,7 @@
fences[i].second->signalForTest(kCompositeTime.ns() + static_cast<nsecs_t>(i));
}
- const auto fence = makePendingFence(fenceMap);
+ const auto fence = fenceMap.makePendingFenceForTest();
// ...then the present latency is measured using the latest frame.
constexpr Duration kPresentLatency = Duration::fromNs(static_cast<nsecs_t>(kPresentCount) - 1);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e2e89ad..8ffc3c3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -77,6 +77,7 @@
#include <processgroup/processgroup.h>
#include <renderengine/RenderEngine.h>
#include <renderengine/impl/ExternalTexture.h>
+#include <scheduler/FrameTargeter.h>
#include <sys/types.h>
#include <ui/ColorSpace.h>
#include <ui/DataspaceUtils.h>
@@ -96,6 +97,7 @@
#include <utils/Timers.h>
#include <utils/misc.h>
+#include <unistd.h>
#include <algorithm>
#include <cerrno>
#include <cinttypes>
@@ -909,6 +911,27 @@
ALOGE("Run StartPropertySetThread failed!");
}
+ if (mTransactionTracing) {
+ TransactionTraceWriter::getInstance().setWriterFunction([&](const std::string& prefix,
+ bool overwrite) {
+ auto writeFn = [&]() {
+ const std::string filename =
+ TransactionTracing::DIR_NAME + prefix + TransactionTracing::FILE_NAME;
+ if (overwrite && access(filename.c_str(), F_OK) == 0) {
+ ALOGD("TransactionTraceWriter: file=%s already exists", filename.c_str());
+ return;
+ }
+ mTransactionTracing->flush();
+ mTransactionTracing->writeToFile(filename);
+ };
+ if (std::this_thread::get_id() == mMainThreadId) {
+ writeFn();
+ } else {
+ mScheduler->schedule(writeFn).get();
+ }
+ });
+ }
+
ALOGV("Done initializing");
}
@@ -2139,44 +2162,6 @@
}
}
-bool SurfaceFlinger::wouldPresentEarly(TimePoint frameTime, Period vsyncPeriod) const {
- const bool isThreeVsyncsAhead = mExpectedPresentTime - frameTime > 2 * vsyncPeriod;
- return isThreeVsyncsAhead ||
- getPreviousPresentFence(frameTime, vsyncPeriod)->getSignalTime() !=
- Fence::SIGNAL_TIME_PENDING;
-}
-
-auto SurfaceFlinger::getPreviousPresentFence(TimePoint frameTime, Period vsyncPeriod) const
- -> const FenceTimePtr& {
- const bool isTwoVsyncsAhead = mExpectedPresentTime - frameTime > vsyncPeriod;
- const size_t i = static_cast<size_t>(isTwoVsyncsAhead);
- return mPreviousPresentFences[i].fenceTime;
-}
-
-bool SurfaceFlinger::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
- ATRACE_CALL();
- if (fence == FenceTime::NO_FENCE) {
- return false;
- }
-
- const status_t status = fence->wait(graceTimeMs);
- // This is the same as Fence::Status::Unsignaled, but it saves a getStatus() call,
- // which calls wait(0) again internally
- return status == -ETIME;
-}
-
-TimePoint SurfaceFlinger::calculateExpectedPresentTime(TimePoint frameTime) const {
- const auto& schedule = mScheduler->getVsyncSchedule();
-
- const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(frameTime);
- if (mScheduler->vsyncModulator().getVsyncConfig().sfOffset > 0) {
- return vsyncDeadline;
- }
-
- // Inflate the expected present time if we're targeting the next vsync.
- return vsyncDeadline + schedule->period();
-}
-
void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) {
Mutex::Autolock lock(mStateLock);
if (configureLocked()) {
@@ -2187,6 +2172,7 @@
bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
bool transactionsFlushed,
bool& outTransactionsAreEmpty) {
+ ATRACE_CALL();
bool needsTraversal = false;
if (transactionsFlushed) {
needsTraversal |= commitMirrorDisplays(vsyncId);
@@ -2239,7 +2225,7 @@
bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update,
bool transactionsFlushed, bool& outTransactionsAreEmpty) {
using Changes = frontend::RequestedLayerState::Changes;
- ATRACE_NAME("updateLayerSnapshots");
+ ATRACE_CALL();
{
mLayerLifecycleManager.addLayers(std::move(update.newLayers));
mLayerLifecycleManager.applyTransactions(update.transactions);
@@ -2350,75 +2336,15 @@
return mustComposite;
}
-bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime)
+bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget)
FTL_FAKE_GUARD(kMainThreadContext) {
- // The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the
- // future relative to frameTime, but may not be for delayed frames. Adjust mExpectedPresentTime
- // accordingly, but not mScheduledPresentTime.
- const TimePoint lastScheduledPresentTime = mScheduledPresentTime;
- mScheduledPresentTime = expectedVsyncTime;
+ const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
+ ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
- // Calculate the expected present time once and use the cached value throughout this frame to
- // make sure all layers are seeing this same value.
- mExpectedPresentTime = expectedVsyncTime >= frameTime ? expectedVsyncTime
- : calculateExpectedPresentTime(frameTime);
-
- ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(vsyncId),
- ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
- mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
-
- const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
- const FenceTimePtr& previousPresentFence = getPreviousPresentFence(frameTime, vsyncPeriod);
-
- // When backpressure propagation is enabled, we want to give a small grace period of 1ms
- // for the present fence to fire instead of just giving up on this frame to handle cases
- // where present fence is just about to get signaled.
- const int graceTimeForPresentFenceMs = static_cast<int>(
- mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu));
-
- // Pending frames may trigger backpressure propagation.
- const TracedOrdinal<bool> framePending = {"PrevFramePending",
- isFencePending(previousPresentFence,
- graceTimeForPresentFenceMs)};
-
- // Frame missed counts for metrics tracking.
- // A frame is missed if the prior frame is still pending. If no longer pending,
- // then we still count the frame as missed if the predicted present time
- // was further in the past than when the fence actually fired.
-
- // Add some slop to correct for drift. This should generally be
- // smaller than a typical frame duration, but should not be so small
- // that it reports reasonable drift as a missed frame.
- const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
- const nsecs_t previousPresentTime = previousPresentFence->getSignalTime();
- const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
- framePending ||
- (previousPresentTime >= 0 &&
- (lastScheduledPresentTime.ns() <
- previousPresentTime - frameMissedSlop))};
- const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
- frameMissed &&
- mCompositionCoverage.test(
- CompositionCoverage::Hwc)};
-
- const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
- frameMissed &&
- mCompositionCoverage.test(
- CompositionCoverage::Gpu)};
-
- if (frameMissed) {
- mFrameMissedCount++;
+ if (pacesetterFrameTarget.didMissFrame()) {
mTimeStats->incrementMissedFrames();
}
- if (hwcFrameMissed) {
- mHwcFrameMissedCount++;
- }
-
- if (gpuFrameMissed) {
- mGpuFrameMissedCount++;
- }
-
if (mTracingEnabledChanged) {
mLayerTracingEnabled = mLayerTracing.isEnabled();
mTracingEnabledChanged = false;
@@ -2427,7 +2353,7 @@
// If we are in the middle of a mode change and the fence hasn't
// fired yet just wait for the next commit.
if (mSetActiveModePending) {
- if (framePending) {
+ if (pacesetterFrameTarget.isFramePending()) {
mScheduler->scheduleFrame();
return false;
}
@@ -2441,26 +2367,29 @@
}
}
- if (framePending) {
- if (mBackpressureGpuComposition || (hwcFrameMissed && !gpuFrameMissed)) {
+ if (pacesetterFrameTarget.isFramePending()) {
+ if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) {
scheduleCommit(FrameHint::kNone);
return false;
}
}
+ const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
+
// Save this once per commit + composite to ensure consistency
// TODO (b/240619471): consider removing active display check once AOD is fixed
const auto activeDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayId));
mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
activeDisplay->getPowerMode() == hal::PowerMode::ON;
if (mPowerHintSessionEnabled) {
- mPowerAdvisor->setCommitStart(frameTime);
- mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
+ mPowerAdvisor->setCommitStart(pacesetterFrameTarget.frameBeginTime());
+ mPowerAdvisor->setExpectedPresentTime(pacesetterFrameTarget.expectedPresentTime());
// Frame delay is how long we should have minus how long we actually have.
const Duration idealSfWorkDuration =
mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration;
- const Duration frameDelay = idealSfWorkDuration - (mExpectedPresentTime - frameTime);
+ const Duration frameDelay =
+ idealSfWorkDuration - pacesetterFrameTarget.expectedFrameDuration();
mPowerAdvisor->setFrameDelay(frameDelay);
mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
@@ -2480,7 +2409,8 @@
// Composite if transactions were committed, or if requested by HWC.
bool mustComposite = mMustComposite.exchange(false);
{
- mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId), frameTime.ns(),
+ mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId),
+ pacesetterFrameTarget.frameBeginTime().ns(),
Fps::fromPeriodNsecs(vsyncPeriod.ns()));
const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
@@ -2488,10 +2418,11 @@
if (flushTransactions) {
updates = flushLifecycleUpdates();
if (mTransactionTracing) {
- mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId),
- frameTime.ns(), updates,
- mFrontEndDisplayInfos,
- mFrontEndDisplayInfosChanged);
+ mTransactionTracing
+ ->addCommittedTransactions(ftl::to_underlying(vsyncId),
+ pacesetterFrameTarget.frameBeginTime().ns(),
+ updates, mFrontEndDisplayInfos,
+ mFrontEndDisplayInfosChanged);
}
}
bool transactionsAreEmpty;
@@ -2530,11 +2461,11 @@
}
updateCursorAsync();
- updateInputFlinger(vsyncId, frameTime);
+ updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime());
if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and tracing should only be enabled for debugging.
- addToLayerTracing(mVisibleRegionsDirty, frameTime, vsyncId);
+ addToLayerTracing(mVisibleRegionsDirty, pacesetterFrameTarget.frameBeginTime(), vsyncId);
}
mLastCommittedVsyncId = vsyncId;
@@ -2543,26 +2474,31 @@
return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
}
-void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId)
+CompositeResult SurfaceFlinger::composite(scheduler::FrameTargeter& pacesetterFrameTargeter)
FTL_FAKE_GUARD(kMainThreadContext) {
+ const scheduler::FrameTarget& pacesetterFrameTarget = pacesetterFrameTargeter.target();
+
+ const VsyncId vsyncId = pacesetterFrameTarget.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());
std::vector<DisplayId> displayIds;
for (const auto& [_, display] : displays) {
- bool dropFrame = false;
- if (display->isVirtual()) {
- Fps refreshRate = display->getAdjustedRefreshRate();
- using fps_approx_ops::operator>;
- dropFrame = (refreshRate > 0_Hz) && !mScheduler->isVsyncInPhase(frameTime, refreshRate);
- }
- if (!dropFrame) {
- refreshArgs.outputs.push_back(display->getCompositionDisplay());
- }
- display->tracePowerMode();
displayIds.push_back(display->getId());
+ display->tracePowerMode();
+
+ if (display->isVirtual()) {
+ const Fps refreshRate = display->getAdjustedRefreshRate();
+ if (refreshRate.isValid() &&
+ !mScheduler->isVsyncInPhase(pacesetterFrameTarget.frameBeginTime(), refreshRate)) {
+ continue;
+ }
+ }
+
+ refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
mPowerAdvisor->setDisplays(displayIds);
@@ -2622,15 +2558,15 @@
if (!getHwComposer().getComposer()->isSupported(
Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
- wouldPresentEarly(frameTime, vsyncPeriod)) {
- const auto prevVsyncTime = mExpectedPresentTime - vsyncPeriod;
+ pacesetterFrameTarget.wouldPresentEarly(vsyncPeriod)) {
const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
- refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
+ refreshArgs.earliestPresentTime =
+ pacesetterFrameTarget.previousFrameVsyncTime(vsyncPeriod) - hwcMinWorkDuration;
}
refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
- refreshArgs.expectedPresentTime = mExpectedPresentTime.ns();
+ refreshArgs.expectedPresentTime = pacesetterFrameTarget.expectedPresentTime().ns();
refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
// Store the present time just before calling to the composition engine so we could notify
@@ -2656,14 +2592,14 @@
}
}
- mTimeStats->recordFrameDuration(frameTime.ns(), systemTime());
+ mTimeStats->recordFrameDuration(pacesetterFrameTarget.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 = mPreviousPresentFences[0].fenceTime;
+ const auto& previousPresentFence = pacesetterFrameTarget.presentFenceForPreviousFrame();
mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(previousPresentFence->getSignalTime()),
TimePoint::now());
@@ -2674,7 +2610,7 @@
scheduleComposite(FrameHint::kNone);
}
- postComposition(presentTime);
+ postComposition(pacesetterFrameTargeter, presentTime);
const bool hadGpuComposited = mCompositionCoverage.test(CompositionCoverage::Gpu);
mCompositionCoverage.clear();
@@ -2717,7 +2653,7 @@
mLayersWithQueuedFrames.clear();
if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and should only be used for debugging.
- addToLayerTracing(mVisibleRegionsDirty, frameTime, vsyncId);
+ addToLayerTracing(mVisibleRegionsDirty, pacesetterFrameTarget.frameBeginTime(), vsyncId);
}
if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
@@ -2730,6 +2666,8 @@
if (mPowerHintSessionEnabled) {
mPowerAdvisor->setCompositeEnd(TimePoint::now());
}
+
+ return {mCompositionCoverage};
}
void SurfaceFlinger::updateLayerGeometry() {
@@ -2813,7 +2751,8 @@
return ui::ROTATION_0;
}
-void SurfaceFlinger::postComposition(nsecs_t callTime) {
+void SurfaceFlinger::postComposition(scheduler::FrameTargeter& pacesetterFrameTargeter,
+ nsecs_t presentStartTime) {
ATRACE_CALL();
ALOGV(__func__);
@@ -2830,15 +2769,11 @@
glCompositionDoneFenceTime = FenceTime::NO_FENCE;
}
- mPreviousPresentFences[1] = mPreviousPresentFences[0];
-
auto presentFence = defaultDisplay
? getHwComposer().getPresentFence(defaultDisplay->getPhysicalId())
: Fence::NO_FENCE;
- auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
- mPreviousPresentFences[0] = {presentFence, presentFenceTime};
-
+ auto presentFenceTime = pacesetterFrameTargeter.setPresentFence(presentFence);
const TimePoint presentTime = TimePoint::now();
// Set presentation information before calling Layer::releasePendingBuffer, such that jank
@@ -3021,7 +2956,7 @@
if (!layer->hasTrustedPresentationListener()) {
return;
}
- const frontend::LayerSnapshot* snapshot = (mLayerLifecycleManagerEnabled)
+ const frontend::LayerSnapshot* snapshot = mLayerLifecycleManagerEnabled
? mLayerSnapshotBuilder.getSnapshot(layer->sequence)
: layer->getLayerSnapshot();
std::optional<const DisplayDevice*> displayOpt = std::nullopt;
@@ -3030,7 +2965,8 @@
}
const DisplayDevice* display = displayOpt.value_or(nullptr);
layer->updateTrustedPresentationState(display, snapshot,
- nanoseconds_to_milliseconds(callTime), false);
+ nanoseconds_to_milliseconds(presentStartTime),
+ false);
});
}
@@ -3914,6 +3850,10 @@
mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
}
+void SurfaceFlinger::notifyCpuLoadUp() {
+ mPowerAdvisor->notifyCpuLoadUp();
+}
+
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
using namespace scheduler;
@@ -3941,6 +3881,9 @@
if (display->refreshRateSelector().kernelIdleTimerController()) {
features |= Feature::kKernelIdleTimer;
}
+ if (mBackpressureGpuComposition) {
+ features |= Feature::kBackpressureGpuComposition;
+ }
auto modulatorPtr = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
@@ -4258,33 +4201,38 @@
TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelineCheck(
const TransactionHandler::TransactionFlushState& flushState) {
- using TransactionReadiness = TransactionHandler::TransactionReadiness;
const auto& transaction = *flushState.transaction;
- TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
+
+ const TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
+ const TimePoint expectedPresentTime = mScheduler->pacesetterFrameTarget().expectedPresentTime();
+
+ using TransactionReadiness = TransactionHandler::TransactionReadiness;
+
// Do not present if the desiredPresentTime has not passed unless it is more than
// one second in the future. We ignore timestamps more than 1 second in the future
// for stability reasons.
- if (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
- desiredPresentTime < mExpectedPresentTime + 1s) {
+ if (!transaction.isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
+ desiredPresentTime < expectedPresentTime + 1s) {
ATRACE_FORMAT("not current desiredPresentTime: %" PRId64 " expectedPresentTime: %" PRId64,
- desiredPresentTime, mExpectedPresentTime);
+ desiredPresentTime, expectedPresentTime);
return TransactionReadiness::NotReady;
}
- if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) {
- ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d",
- mExpectedPresentTime, transaction.originUid);
+ if (!mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) {
+ ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d", expectedPresentTime,
+ transaction.originUid);
return TransactionReadiness::NotReady;
}
// If the client didn't specify desiredPresentTime, use the vsyncId to determine the
// expected present time of this transaction.
if (transaction.isAutoTimestamp &&
- frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
+ frameIsEarly(expectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64,
- transaction.frameTimelineInfo.vsyncId, mExpectedPresentTime);
+ transaction.frameTimelineInfo.vsyncId, expectedPresentTime);
return TransactionReadiness::NotReady;
}
+
return TransactionReadiness::Ready;
}
@@ -6024,10 +5972,6 @@
dumpVsync(result);
result.append("\n");
- StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load());
- StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load());
- StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load());
-
/*
* Dump the visible layer list
*/
@@ -6728,7 +6672,7 @@
mTransactionTracing->setBufferSize(
TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
} else {
- mTransactionTracing->writeToFile();
+ TransactionTraceWriter::getInstance().invoke("", /* overwrite= */ true);
mTransactionTracing->setBufferSize(
TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
}
@@ -7408,6 +7352,13 @@
renderArea->getHintForSeamlessTransition());
sdrWhitePointNits = state.sdrWhitePointNits;
displayBrightnessNits = state.displayBrightnessNits;
+ if (sdrWhitePointNits > 1.0f) {
+ // 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;
+ displayBrightnessNits =
+ std::min(sdrWhitePointNits * kMaxScreenshotHeadroom, displayBrightnessNits);
+ }
if (requestedDataspace == ui::Dataspace::UNKNOWN) {
renderIntent = state.renderIntent;
@@ -8214,7 +8165,7 @@
if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
return;
}
- if (uid != CaptureArgs::UNSET_UID && snapshot->uid != uid) {
+ if (uid != CaptureArgs::UNSET_UID && snapshot->uid != gui::Uid(uid)) {
return;
}
if (!snapshot->hasSomethingToDraw()) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b7d2047..e27d21f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -194,7 +194,8 @@
private IBinder::DeathRecipient,
private HWC2::ComposerCallback,
private ICompositor,
- private scheduler::ISchedulerCallback {
+ private scheduler::ISchedulerCallback,
+ private compositionengine::ICEPowerCallback {
public:
struct SkipInitializationTag {};
@@ -632,8 +633,8 @@
// ICompositor overrides:
void configure() override;
- bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) override;
- void composite(TimePoint frameTime, VsyncId) override;
+ bool commit(const scheduler::FrameTarget&) override;
+ CompositeResult composite(scheduler::FrameTargeter&) override;
void sample() override;
// ISchedulerCallback overrides:
@@ -642,6 +643,9 @@
void kernelTimerChanged(bool expired) override;
void triggerOnFrameRateOverridesChanged() override;
+ // ICEPowerCallback overrides:
+ void notifyCpuLoadUp() override;
+
// Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
void toggleKernelIdleTimer() REQUIRES(mStateLock);
@@ -952,7 +956,8 @@
/*
* Compositing
*/
- void postComposition(nsecs_t callTime) REQUIRES(kMainThreadContext);
+ void postComposition(scheduler::FrameTargeter&, nsecs_t presentStartTime)
+ REQUIRES(kMainThreadContext);
/*
* Display management
@@ -993,20 +998,6 @@
*/
nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
- using FenceTimePtr = std::shared_ptr<FenceTime>;
-
- bool wouldPresentEarly(TimePoint frameTime, Period) const REQUIRES(kMainThreadContext);
-
- const FenceTimePtr& getPreviousPresentFence(TimePoint frameTime, Period) const
- REQUIRES(kMainThreadContext);
-
- // Blocks the thread waiting for up to graceTimeMs in case the fence is about to signal.
- static bool isFencePending(const FenceTimePtr&, int graceTimeMs);
-
- // Calculates the expected present time for this frame. For negative offsets, performs a
- // correction using the predicted vsync for the next frame instead.
- TimePoint calculateExpectedPresentTime(TimePoint frameTime) const;
-
/*
* Display identification
*/
@@ -1252,9 +1243,6 @@
// If blurs should be enabled on this device.
bool mSupportsBlur = false;
- std::atomic<uint32_t> mFrameMissedCount = 0;
- std::atomic<uint32_t> mHwcFrameMissedCount = 0;
- std::atomic<uint32_t> mGpuFrameMissedCount = 0;
TransactionCallbackInvoker mTransactionCallbackInvoker;
@@ -1322,15 +1310,6 @@
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
- struct FenceWithFenceTime {
- sp<Fence> fence = Fence::NO_FENCE;
- FenceTimePtr fenceTime = FenceTime::NO_FENCE;
- };
- std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
-
- TimePoint mScheduledPresentTime GUARDED_BY(kMainThreadContext);
- TimePoint mExpectedPresentTime GUARDED_BY(kMainThreadContext);
-
// below flags are set by main thread only
bool mSetActiveModePending = false;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 20fa091..96c8b54 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -375,5 +375,9 @@
return SurfaceFlingerProperties::ignore_hdr_camera_layers().value_or(defaultValue);
}
+bool clear_slots_with_set_layer_buffer(bool defaultValue) {
+ return SurfaceFlingerProperties::clear_slots_with_set_layer_buffer().value_or(defaultValue);
+}
+
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 080feee..951f8f8 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -102,6 +102,8 @@
bool ignore_hdr_camera_layers(bool defaultValue);
+bool clear_slots_with_set_layer_buffer(bool defaultValue);
+
} // namespace sysprop
} // namespace android
#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index 155a275..fc6c4f3 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -1,4 +1,9 @@
{
+ "imports": [
+ {
+ "path": "frameworks/native/libs/gui"
+ }
+ ],
"presubmit": [
{
"name": "libsurfaceflinger_unittest"
@@ -7,14 +12,6 @@
"name": "libcompositionengine_test"
},
{
- "name": "libgui_test",
- "options": [
- {
- "native-test-flag": "--gtest_filter=\"InputSurfacesTest*:MultiDisplayTests*\""
- }
- ]
- },
- {
"name": "libscheduler_test"
}
],
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index dafdc8a..b1e3d63 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -195,6 +195,7 @@
windowInfoProto->set_layout_params_flags(inputInfo->layoutParamsFlags.get());
windowInfoProto->set_layout_params_type(
static_cast<int32_t>(inputInfo->layoutParamsType));
+ windowInfoProto->set_input_config(inputInfo->inputConfig.get());
LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
windowInfoProto->mutable_touchable_region());
windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
@@ -467,11 +468,9 @@
static_cast<gui::WindowInfo::Type>(windowInfoProto.layout_params_type());
LayerProtoHelper::readFromProto(windowInfoProto.touchable_region(),
inputInfo.touchableRegion);
+ inputInfo.inputConfig =
+ ftl::Flags<gui::WindowInfo::InputConfig>(windowInfoProto.input_config());
inputInfo.surfaceInset = windowInfoProto.surface_inset();
- inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_FOCUSABLE,
- !windowInfoProto.focusable());
- inputInfo.setInputConfig(gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER,
- windowInfoProto.has_wallpaper());
inputInfo.globalScaleFactor = windowInfoProto.global_scale_factor();
const proto::Transform& transformProto = windowInfoProto.transform();
inputInfo.transform.set(transformProto.dsdx(), transformProto.dtdx(), transformProto.dtdy(),
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 632de01..7e330b9 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -28,6 +28,7 @@
#include "TransactionTracing.h"
namespace android {
+ANDROID_SINGLETON_STATIC_INSTANCE(android::TransactionTraceWriter)
TransactionTracing::TransactionTracing()
: mProtoParser(std::make_unique<TransactionProtoParser::FlingerDataMapper>()) {
@@ -56,7 +57,7 @@
writeToFile();
}
-status_t TransactionTracing::writeToFile(std::string filename) {
+status_t TransactionTracing::writeToFile(const std::string& filename) {
std::scoped_lock lock(mTraceLock);
proto::TransactionTraceFile fileProto = createTraceFileProto();
addStartingStateToProtoLocked(fileProto);
@@ -115,6 +116,7 @@
}
mPendingUpdates.emplace_back(update);
tryPushToTracingThread();
+ mLastUpdatedVsyncId = vsyncId;
}
void TransactionTracing::loop() {
@@ -218,19 +220,29 @@
mTransactionsAddedToBufferCv.notify_one();
}
-void TransactionTracing::flush(int64_t vsyncId) {
- while (!mPendingUpdates.empty() || !mPendingDestroyedLayers.empty()) {
- tryPushToTracingThread();
+void TransactionTracing::flush() {
+ {
+ std::scoped_lock lock(mMainThreadLock);
+ // Collect any pending transactions and wait for transactions to be added to
+ mUpdates.insert(mUpdates.end(), std::make_move_iterator(mPendingUpdates.begin()),
+ std::make_move_iterator(mPendingUpdates.end()));
+ mPendingUpdates.clear();
+ mDestroyedLayers.insert(mDestroyedLayers.end(), mPendingDestroyedLayers.begin(),
+ mPendingDestroyedLayers.end());
+ mPendingDestroyedLayers.clear();
+ mTransactionsAvailableCv.notify_one();
}
std::unique_lock<std::mutex> lock(mTraceLock);
base::ScopedLockAssertion assumeLocked(mTraceLock);
- mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mTraceLock) {
- proto::TransactionTraceEntry entry;
- if (mBuffer.used() > 0) {
- entry.ParseFromString(mBuffer.back());
- }
- return mBuffer.used() > 0 && entry.vsync_id() >= vsyncId;
- });
+ mTransactionsAddedToBufferCv.wait_for(lock, std::chrono::milliseconds(100),
+ [&]() REQUIRES(mTraceLock) {
+ proto::TransactionTraceEntry entry;
+ if (mBuffer.used() > 0) {
+ entry.ParseFromString(mBuffer.back());
+ }
+ return mBuffer.used() > 0 &&
+ entry.vsync_id() >= mLastUpdatedVsyncId;
+ });
}
void TransactionTracing::onLayerRemoved(int32_t layerId) {
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index 0e56627..a59dc6e 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -19,6 +19,7 @@
#include <android-base/thread_annotations.h>
#include <layerproto/TransactionProto.h>
#include <utils/Errors.h>
+#include <utils/Singleton.h>
#include <utils/Timers.h>
#include <memory>
@@ -60,10 +61,12 @@
void addQueuedTransaction(const TransactionState&);
void addCommittedTransactions(int64_t vsyncId, nsecs_t commitTime, frontend::Update& update,
const frontend::DisplayInfos&, bool displayInfoChanged);
- status_t writeToFile(std::string filename = FILE_NAME);
+ status_t writeToFile(const std::string& filename = FILE_PATH);
void setBufferSize(size_t bufferSizeInBytes);
void onLayerRemoved(int layerId);
void dump(std::string&) const;
+ // Wait until all the committed transactions for the specified vsync id are added to the buffer.
+ void flush() EXCLUDES(mMainThreadLock);
static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024;
static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;
// version 1 - switching to support new frontend
@@ -73,7 +76,9 @@
friend class TransactionTracingTest;
friend class SurfaceFlinger;
- static constexpr auto FILE_NAME = "/data/misc/wmtrace/transactions_trace.winscope";
+ static constexpr auto DIR_NAME = "/data/misc/wmtrace/";
+ static constexpr auto FILE_NAME = "transactions_trace.winscope";
+ static constexpr auto FILE_PATH = "/data/misc/wmtrace/transactions_trace.winscope";
mutable std::mutex mTraceLock;
RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry> mBuffer
@@ -111,6 +116,7 @@
std::vector<uint32_t /* layerId */> mDestroyedLayers GUARDED_BY(mMainThreadLock);
std::vector<uint32_t /* layerId */> mPendingDestroyedLayers; // only accessed by main thread
+ int64_t mLastUpdatedVsyncId = -1;
proto::TransactionTraceFile createTraceFileProto() const;
void loop();
@@ -121,10 +127,21 @@
void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
// TEST
- // Wait until all the committed transactions for the specified vsync id are added to the buffer.
- void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock);
// Return buffer contents as trace file proto
proto::TransactionTraceFile writeToProto() EXCLUDES(mMainThreadLock);
};
+class TransactionTraceWriter : public Singleton<TransactionTraceWriter> {
+ friend class Singleton<TransactionTracing>;
+ std::function<void(const std::string& prefix, bool overwrite)> mWriterFunction =
+ [](const std::string&, bool) {};
+
+public:
+ void setWriterFunction(
+ std::function<void(const std::string& prefix, bool overwrite)> function) {
+ mWriterFunction = std::move(function);
+ }
+ void invoke(const std::string& prefix, bool overwrite) { mWriterFunction(prefix, overwrite); }
+};
+
} // namespace android
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 72a11c6..519ef44 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -41,7 +41,7 @@
using namespace ftl::flag_operators;
bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile,
- const char* outputLayersTracePath) {
+ const char* outputLayersTracePath, bool onlyLastEntry) {
if (traceFile.entry_size() == 0) {
ALOGD("Trace file is empty");
return false;
@@ -158,9 +158,11 @@
layerTracing.getFlags())
.generate(hierarchyBuilder.getHierarchy());
auto displayProtos = LayerProtoHelper::writeDisplayInfoToProto(displayInfos);
- layerTracing.notify(visibleRegionsDirty, entry.elapsed_realtime_nanos(), entry.vsync_id(),
- &layersProto, {}, &displayProtos);
- layerTracing.appendToStream(out);
+ if (!onlyLastEntry || (i == traceFile.entry_size() - 1)) {
+ layerTracing.notify(visibleRegionsDirty, entry.elapsed_realtime_nanos(),
+ entry.vsync_id(), &layersProto, {}, &displayProtos);
+ layerTracing.appendToStream(out);
+ }
}
layerTracing.disable("", /*writeToFile=*/false);
out.close();
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
index ee1ea6c..e41d1e6 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
@@ -21,6 +21,7 @@
namespace android {
class LayerTraceGenerator {
public:
- bool generate(const proto::TransactionTraceFile&, const char* outputLayersTracePath);
+ bool generate(const proto::TransactionTraceFile&, const char* outputLayersTracePath,
+ bool onlyLastEntry);
};
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp
index c440c19..5ca87e4 100644
--- a/services/surfaceflinger/Tracing/tools/main.cpp
+++ b/services/surfaceflinger/Tracing/tools/main.cpp
@@ -26,9 +26,9 @@
using namespace android;
int main(int argc, char** argv) {
- if (argc > 3) {
+ if (argc > 4) {
std::cout << "Usage: " << argv[0]
- << " [transaction-trace-path] [output-layers-trace-path]\n";
+ << " [transaction-trace-path] [output-layers-trace-path] [--last-entry-only]\n";
return -1;
}
@@ -48,12 +48,16 @@
}
const char* outputLayersTracePath =
- (argc == 3) ? argv[2] : "/data/misc/wmtrace/layers_trace.winscope";
- ;
+ (argc >= 3) ? argv[2] : "/data/misc/wmtrace/layers_trace.winscope";
+
+ const bool generateLastEntryOnly =
+ argc >= 4 && std::string_view(argv[3]) == "--last-entry-only";
+
ALOGD("Generating %s...", outputLayersTracePath);
std::cout << "Generating " << outputLayersTracePath << "\n";
- if (!LayerTraceGenerator().generate(transactionTraceFile, outputLayersTracePath)) {
+ if (!LayerTraceGenerator().generate(transactionTraceFile, outputLayersTracePath,
+ generateLastEntryOnly)) {
std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath;
return -1;
}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 8e208bc..0c9a16b 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -286,8 +286,8 @@
private:
// ICompositor overrides:
void configure() override {}
- bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
- void composite(TimePoint, VsyncId) override {}
+ bool commit(const scheduler::FrameTarget&) override { return false; }
+ CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
void sample() override {}
// MessageQueue overrides:
@@ -604,7 +604,9 @@
mFlinger->commitTransactions();
mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
- mFlinger->postComposition(systemTime());
+
+ scheduler::FrameTargeter frameTargeter(mFdp.ConsumeBool());
+ mFlinger->postComposition(frameTargeter, mFdp.ConsumeIntegral<nsecs_t>());
}
mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
@@ -622,8 +624,6 @@
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mFdp.ConsumeIntegral<uid_t>());
- mFlinger->calculateExpectedPresentTime({});
-
mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool());
fuzzDumpsysAndDebug(&mFdp);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 921cae4..9f0bdde 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -106,7 +106,7 @@
effectLayer->addSurfaceFramePresentedForBuffer(surfaceFrame,
mFdp.ConsumeIntegral<int64_t>() /*acquireTime*/,
mFdp.ConsumeIntegral<int64_t>() /*currentTime*/);
- effectLayer->addSurfaceFrameDroppedForBuffer(surfaceFrame1);
+ effectLayer->addSurfaceFrameDroppedForBuffer(surfaceFrame1, mFdp.ConsumeIntegral<nsecs_t>());
parent.clear();
client.clear();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index f17d2e1..b1fd06f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -19,6 +19,7 @@
#include <fuzzer/FuzzedDataProvider.h>
#include <processgroup/sched_policy.h>
+#include <scheduler/IVsyncSource.h>
#include <scheduler/PresentLatencyTracker.h>
#include "Scheduler/OneShotTimer.h"
@@ -42,6 +43,7 @@
(120_Hz).getPeriodNsecs()};
constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>();
+constexpr auto kCompositionCoverage = ftl::enum_range<CompositionCoverage>();
constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF,
PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND};
@@ -56,6 +58,10 @@
component->dump(res);
}
+inline sp<Fence> makeFakeFence() {
+ return sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING));
+}
+
class SchedulerFuzzer {
public:
SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
@@ -65,6 +71,7 @@
void fuzzRefreshRateSelection();
void fuzzRefreshRateSelector();
void fuzzPresentLatencyTracker();
+ void fuzzFrameTargeter();
void fuzzVSyncModulator();
void fuzzVSyncPredictor();
void fuzzVSyncReactor();
@@ -256,13 +263,13 @@
reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed);
reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt,
&periodFlushed);
- sp<Fence> fence = sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING));
- std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+
+ const auto fence = std::make_shared<FenceTime>(makeFakeFence());
vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>());
FenceTime::Snapshot snap(mFdp.ConsumeIntegral<nsecs_t>());
- ft->applyTrustedSnapshot(snap);
+ fence->applyTrustedSnapshot(snap);
reactor.setIgnorePresentFences(mFdp.ConsumeBool());
- reactor.addPresentFence(ft);
+ reactor.addPresentFence(fence);
dump<scheduler::VSyncReactor>(&reactor, &mFdp);
}
@@ -392,14 +399,45 @@
void SchedulerFuzzer::fuzzPresentLatencyTracker() {
scheduler::PresentLatencyTracker tracker;
- tracker.trackPendingFrame(TimePoint::fromNs(mFdp.ConsumeIntegral<nsecs_t>()),
- FenceTime::NO_FENCE);
+
+ int i = 5;
+ while (i-- > 0) {
+ tracker.trackPendingFrame(getFuzzedTimePoint(mFdp),
+ std::make_shared<FenceTime>(makeFakeFence()));
+ }
+}
+
+void SchedulerFuzzer::fuzzFrameTargeter() {
+ scheduler::FrameTargeter frameTargeter(mFdp.ConsumeBool());
+
+ const struct VsyncSource final : scheduler::IVsyncSource {
+ explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {}
+ FuzzedDataProvider& fuzzer;
+
+ Period period() const { return getFuzzedDuration(fuzzer); }
+ TimePoint vsyncDeadlineAfter(TimePoint) const { return getFuzzedTimePoint(fuzzer); }
+ } vsyncSource{mFdp};
+
+ int i = 10;
+ while (i-- > 0) {
+ frameTargeter.beginFrame({.frameBeginTime = getFuzzedTimePoint(mFdp),
+ .vsyncId = getFuzzedVsyncId(mFdp),
+ .expectedVsyncTime = getFuzzedTimePoint(mFdp),
+ .sfWorkDuration = getFuzzedDuration(mFdp)},
+ vsyncSource);
+
+ frameTargeter.setPresentFence(makeFakeFence());
+
+ frameTargeter.endFrame(
+ {.compositionCoverage = mFdp.PickValueInArray(kCompositionCoverage.values)});
+ }
}
void SchedulerFuzzer::process() {
fuzzRefreshRateSelection();
fuzzRefreshRateSelector();
fuzzPresentLatencyTracker();
+ fuzzFrameTargeter();
fuzzVSyncModulator();
fuzzVSyncPredictor();
fuzzVSyncReactor();
diff --git a/services/surfaceflinger/layerproto/common.proto b/services/surfaceflinger/layerproto/common.proto
index a6d8d61..5e20d4d 100644
--- a/services/surfaceflinger/layerproto/common.proto
+++ b/services/surfaceflinger/layerproto/common.proto
@@ -70,6 +70,7 @@
bool replace_touchable_region_with_crop = 14;
RectProto touchable_region_crop = 15;
TransformProto transform = 16;
+ uint32 input_config = 17;
}
message BlurRegion {
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index b0cee9b..d03afa0 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -256,13 +256,14 @@
int32 layout_params_type = 2;
RegionProto touchable_region = 3;
int32 surface_inset = 4;
- bool focusable = 5;
- bool has_wallpaper = 6;
+ bool focusable = 5; // unused
+ bool has_wallpaper = 6; // unused
float global_scale_factor = 7;
uint32 crop_layer_id = 8;
bool replace_touchable_region_with_crop = 9;
RectProto touchable_region_crop = 10;
Transform transform = 11;
+ uint32 input_config = 12;
}
WindowInfo window_info_handle = 27;
float bg_color_alpha = 28;
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index bcbe21a..689f51a 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -470,4 +470,18 @@
scope: Public
access: Readonly
prop_name: "ro.surface_flinger.ignore_hdr_camera_layers"
-}
\ No newline at end of file
+}
+
+# When enabled, SurfaceFlinger will attempt to clear the per-layer HAL buffer cache slots for
+# buffers when they are evicted from the app cache by using additional setLayerBuffer commands.
+# Ideally, this behavior would always be enabled to reduce graphics memory consumption. However,
+# Some HAL implementations may not support the additional setLayerBuffer commands used to clear
+# the cache slots.
+prop {
+ api_name: "clear_slots_with_set_layer_buffer"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.clear_slots_with_set_layer_buffer"
+}
+
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 348a462..9660ff3 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -1,6 +1,10 @@
props {
module: "android.sysprop.SurfaceFlingerProperties"
prop {
+ api_name: "clear_slots_with_set_layer_buffer"
+ prop_name: "ro.surface_flinger.clear_slots_with_set_layer_buffer"
+ }
+ prop {
api_name: "color_space_agnostic_dataspace"
type: Long
prop_name: "ro.surface_flinger.color_space_agnostic_dataspace"
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
index 2b29530..b8a5e79 100644
--- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -59,8 +59,8 @@
std::string actualLayersTracePath =
std::string(temp_dir.path) + "/" + expectedLayersFilename + "_actual";
- EXPECT_TRUE(
- LayerTraceGenerator().generate(mTransactionTrace, actualLayersTracePath.c_str()))
+ EXPECT_TRUE(LayerTraceGenerator().generate(mTransactionTrace, actualLayersTracePath.c_str(),
+ /*onlyLastEntry=*/true))
<< "Failed to generate layers trace from " << transactionTracePath;
EXPECT_TRUE(std::filesystem::exists(std::filesystem::path(actualLayersTracePath)));
parseLayersTraceFromFile(actualLayersTracePath.c_str(), mActualLayersTraceProto);
@@ -86,9 +86,9 @@
std::vector<std::filesystem::path> TransactionTraceTestSuite::sTransactionTraces{};
struct LayerInfo {
- int32_t id;
+ uint64_t id;
std::string name;
- int32_t parent;
+ uint64_t parent;
int z;
uint64_t curr_frame;
float x;
@@ -119,8 +119,8 @@
}
struct find_id : std::unary_function<LayerInfo, bool> {
- int id;
- find_id(int id) : id(id) {}
+ uint64_t id;
+ find_id(uint64_t id) : id(id) {}
bool operator()(LayerInfo const& m) const { return m.id == id; }
};
@@ -136,9 +136,9 @@
touchableRegionBounds = touchableRegion.bounds();
}
- return {proto.id(),
+ return {static_cast<uint64_t>(proto.id()),
proto.name(),
- proto.parent(),
+ static_cast<uint64_t>(proto.parent()),
proto.z(),
proto.curr_frame(),
proto.has_position() ? proto.position().x() : -1,
@@ -150,7 +150,7 @@
static std::vector<LayerInfo> getLayerInfosFromProto(
android::surfaceflinger::LayersTraceProto& entry) {
- std::unordered_map<int32_t /* snapshotId*/, int32_t /*layerId*/> snapshotIdToLayerId;
+ std::unordered_map<uint64_t /* snapshotId*/, uint64_t /*layerId*/> snapshotIdToLayerId;
std::vector<LayerInfo> layers;
layers.reserve(static_cast<size_t>(entry.layers().layers_size()));
bool mapSnapshotIdToLayerId = false;
@@ -158,7 +158,12 @@
auto layer = entry.layers().layers(i);
LayerInfo layerInfo = getLayerInfoFromProto(layer);
- snapshotIdToLayerId[layerInfo.id] = static_cast<int32_t>(layer.original_id());
+ uint64_t layerId = layerInfo.name.find("(Mirror)") == std::string::npos
+ ? static_cast<uint64_t>(layer.original_id())
+ : static_cast<uint64_t>(layer.original_id()) | 1ull << 63;
+
+ snapshotIdToLayerId[layerInfo.id] = layerId;
+
if (layer.original_id() != 0) {
mapSnapshotIdToLayerId = true;
}
@@ -172,7 +177,7 @@
for (auto& layer : layers) {
layer.id = snapshotIdToLayerId[layer.id];
auto it = snapshotIdToLayerId.find(layer.parent);
- layer.parent = it == snapshotIdToLayerId.end() ? -1 : it->second;
+ layer.parent = it == snapshotIdToLayerId.end() ? static_cast<uint64_t>(-1) : it->second;
}
return layers;
}
@@ -189,7 +194,6 @@
std::vector<LayerInfo> expectedLayers = getLayerInfosFromProto(expectedLastEntry);
std::vector<LayerInfo> actualLayers = getLayerInfosFromProto(actualLastEntry);
- ;
size_t i = 0;
for (; i < actualLayers.size() && i < expectedLayers.size(); i++) {
@@ -197,9 +201,9 @@
find_id(expectedLayers[i].id));
EXPECT_NE(it, actualLayers.end());
EXPECT_EQ(expectedLayers[i], *it);
- ALOGV("Validating %s[%d] parent=%d z=%d frame=%" PRIu64, expectedLayers[i].name.c_str(),
- expectedLayers[i].id, expectedLayers[i].parent, expectedLayers[i].z,
- expectedLayers[i].curr_frame);
+ ALOGV("Validating %s[%" PRIu64 "] parent=%" PRIu64 " z=%d frame=%" PRIu64,
+ expectedLayers[i].name.c_str(), expectedLayers[i].id, expectedLayers[i].parent,
+ expectedLayers[i].z, expectedLayers[i].curr_frame);
}
EXPECT_EQ(expectedLayers.size(), actualLayers.size());
@@ -208,9 +212,9 @@
for (size_t j = 0; j < actualLayers.size(); j++) {
if (std::find_if(expectedLayers.begin(), expectedLayers.end(),
find_id(actualLayers[j].id)) == expectedLayers.end()) {
- ALOGD("actualLayers [%d]:%s parent=%d z=%d frame=%" PRIu64, actualLayers[j].id,
- actualLayers[j].name.c_str(), actualLayers[j].parent, actualLayers[j].z,
- actualLayers[j].curr_frame);
+ ALOGD("actualLayers [%" PRIu64 "]:%s parent=%" PRIu64 " z=%d frame=%" PRIu64,
+ actualLayers[j].id, actualLayers[j].name.c_str(), actualLayers[j].parent,
+ actualLayers[j].z, actualLayers[j].curr_frame);
}
}
FAIL();
@@ -220,9 +224,9 @@
for (size_t j = 0; j < expectedLayers.size(); j++) {
if (std::find_if(actualLayers.begin(), actualLayers.end(),
find_id(expectedLayers[j].id)) == actualLayers.end()) {
- ALOGD("expectedLayers [%d]:%s parent=%d z=%d frame=%" PRIu64, expectedLayers[j].id,
- expectedLayers[j].name.c_str(), expectedLayers[j].parent, expectedLayers[j].z,
- expectedLayers[j].curr_frame);
+ ALOGD("expectedLayers [%" PRIu64 "]:%s parent=%" PRIu64 " z=%d frame=%" PRIu64,
+ expectedLayers[j].id, expectedLayers[j].name.c_str(),
+ expectedLayers[j].parent, expectedLayers[j].z, expectedLayers[j].curr_frame);
}
}
FAIL();
diff --git a/services/surfaceflinger/tests/tracing/readme.md b/services/surfaceflinger/tests/tracing/readme.md
index 3e80a74..f545a3c 100644
--- a/services/surfaceflinger/tests/tracing/readme.md
+++ b/services/surfaceflinger/tests/tracing/readme.md
@@ -14,7 +14,9 @@
#### Workflow ####
Add transaction traces that resulted in front end bugs along
with the layer trace after fixing the issue. The layer trace
-can be generated by using the layertracegenerator tool. The
+can be generated by using the layertracegenerator tool. Use the
+--last-entry-only flag to generate only the last entry in the
+trace. This will keep the test data to a manageable size. The
main goal of this test suite is to add regression tests with
minimal effort.
diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_b282110579.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_b282110579.winscope
new file mode 100644
index 0000000..3246453
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_b282110579.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_b282110579.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_b282110579.winscope
new file mode 100644
index 0000000..ecb9431
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_b282110579.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 12cf070..5da893e 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -228,6 +228,7 @@
setAlpha(1, 0.5);
setAlpha(122, 0.5);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->alpha, 0.5f);
EXPECT_EQ(getSnapshot(12)->alpha, 0.5f);
EXPECT_EQ(getSnapshot(1221)->alpha, 0.25f);
}
@@ -236,28 +237,30 @@
TEST_F(LayerSnapshotTest, UpdateClearsPreviousChangeStates) {
setCrop(1, Rect(1, 2, 3, 4));
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_TRUE(getSnapshot(1)->changes.get() != 0);
- EXPECT_TRUE(getSnapshot(11)->changes.get() != 0);
+ EXPECT_TRUE(getSnapshot(1)->changes.test(RequestedLayerState::Changes::Geometry));
+ EXPECT_TRUE(getSnapshot(11)->changes.test(RequestedLayerState::Changes::Geometry));
setCrop(2, Rect(1, 2, 3, 4));
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_TRUE(getSnapshot(2)->changes.get() != 0);
- EXPECT_TRUE(getSnapshot(1)->changes.get() == 0);
- EXPECT_TRUE(getSnapshot(11)->changes.get() == 0);
+ EXPECT_TRUE(getSnapshot(2)->changes.test(RequestedLayerState::Changes::Geometry));
+ EXPECT_FALSE(getSnapshot(1)->changes.test(RequestedLayerState::Changes::Geometry));
+ EXPECT_FALSE(getSnapshot(11)->changes.test(RequestedLayerState::Changes::Geometry));
}
TEST_F(LayerSnapshotTest, FastPathClearsPreviousChangeStates) {
setColor(11, {1._hf, 0._hf, 0._hf});
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_TRUE(getSnapshot(11)->changes.get() != 0);
- EXPECT_TRUE(getSnapshot(1)->changes.get() == 0);
+ EXPECT_EQ(getSnapshot(11)->changes, RequestedLayerState::Changes::Content);
+ EXPECT_EQ(getSnapshot(11)->clientChanges, layer_state_t::eColorChanged);
+ EXPECT_EQ(getSnapshot(1)->changes.get(), 0u);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_TRUE(getSnapshot(11)->changes.get() == 0);
+ EXPECT_EQ(getSnapshot(11)->changes.get(), 0u);
}
TEST_F(LayerSnapshotTest, FastPathSetsChangeFlagToContent) {
setColor(1, {1._hf, 0._hf, 0._hf});
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(1)->changes, RequestedLayerState::Changes::Content);
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eColorChanged);
}
TEST_F(LayerSnapshotTest, GameMode) {
@@ -270,7 +273,9 @@
transactions.back().states.front().layerId = 1;
transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::GameMode);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged);
EXPECT_EQ(static_cast<int32_t>(getSnapshot(1)->gameMode), 42);
EXPECT_EQ(static_cast<int32_t>(getSnapshot(11)->gameMode), 42);
}
@@ -309,7 +314,7 @@
EXPECT_EQ(getSnapshot(1)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::NoVote);
}
-TEST_F(LayerSnapshotTest, canCropTouchableRegion) {
+TEST_F(LayerSnapshotTest, CanCropTouchableRegion) {
// ROOT
// ├── 1
// │ ├── 11
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 91875cc..359e2ab 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -20,9 +20,10 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <scheduler/interface/ICompositor.h>
+
#include "FrameTimeline.h"
#include "Scheduler/MessageQueue.h"
-#include "SurfaceFlinger.h"
#include "mock/MockVSyncDispatch.h"
namespace android {
@@ -34,8 +35,8 @@
struct NoOpCompositor final : ICompositor {
void configure() override {}
- bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
- void composite(TimePoint, VsyncId) override {}
+ bool commit(const scheduler::FrameTarget&) override { return false; }
+ CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
void sample() override {}
} gNoOpCompositor;
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 4780e49..cf3fab3 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -91,16 +91,18 @@
};
struct DispSyncIsSupportedVariant {
- static void setupStartPeriodTransitionCallExpectations(DisplayTransactionTest* test) {
+ static void setupResetModelCallExpectations(DisplayTransactionTest* test) {
auto vsyncSchedule = test->mFlinger.scheduler()->getVsyncSchedule();
EXPECT_CALL(static_cast<mock::VsyncController&>(vsyncSchedule->getController()),
startPeriodTransition(DEFAULT_VSYNC_PERIOD, false))
.Times(1);
+ EXPECT_CALL(static_cast<mock::VSyncTracker&>(vsyncSchedule->getTracker()), resetModel())
+ .Times(1);
}
};
struct DispSyncNotSupportedVariant {
- static void setupStartPeriodTransitionCallExpectations(DisplayTransactionTest* /* test */) {}
+ static void setupResetModelCallExpectations(DisplayTransactionTest* /* test */) {}
};
// --------------------------------------------------------------------
@@ -123,7 +125,7 @@
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
Case::EventThread::setupEnableVsyncCallExpectations(test);
- Case::DispSync::setupStartPeriodTransitionCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
Case::setupRepaintEverythingCallExpectations(test);
}
@@ -184,7 +186,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::EventThread::setupEnableVsyncCallExpectations(test);
- Case::DispSync::setupStartPeriodTransitionCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
}
};
@@ -202,7 +204,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::EventThread::setupEnableVsyncCallExpectations(test);
- Case::DispSync::setupStartPeriodTransitionCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
}
};
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index a30f7e0..aac11c0 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -173,8 +173,8 @@
private:
// ICompositor overrides:
void configure() override {}
- bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
- void composite(TimePoint, VsyncId) override {}
+ bool commit(const scheduler::FrameTarget&) override { return false; }
+ CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
void sample() override {}
};
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 945e488..833984f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -20,6 +20,11 @@
#include <chrono>
#include <variant>
+#include <ftl/fake_guard.h>
+#include <ftl/match.h>
+#include <gui/ScreenCaptureResults.h>
+#include <ui/DynamicDisplayInfo.h>
+
#include <compositionengine/Display.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
@@ -27,11 +32,7 @@
#include <compositionengine/impl/Display.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/DisplaySurface.h>
-#include <ftl/fake_guard.h>
-#include <ftl/match.h>
-#include <gui/ScreenCaptureResults.h>
-#include <ui/DynamicDisplayInfo.h>
#include "DisplayDevice.h"
#include "FakeVsyncConfiguration.h"
#include "FrameTracer/FrameTracer.h"
@@ -44,7 +45,6 @@
#include "Scheduler/RefreshRateSelector.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlinger.h"
-#include "SurfaceFlingerDefaultFactory.h"
#include "TestableScheduler.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
@@ -360,25 +360,42 @@
commitTransactionsLocked(eDisplayTransactionNeeded);
}
- TimePoint commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) {
- mFlinger->commit(frameTime, vsyncId, expectedVsyncTime);
- return frameTime;
+ void commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime,
+ bool composite = false) {
+ constexpr bool kBackpressureGpuComposition = true;
+ scheduler::FrameTargeter frameTargeter(kBackpressureGpuComposition);
+
+ frameTargeter.beginFrame({.frameBeginTime = frameTime,
+ .vsyncId = vsyncId,
+ .expectedVsyncTime = expectedVsyncTime,
+ .sfWorkDuration = 10ms},
+ *mScheduler->getVsyncSchedule());
+
+ mFlinger->commit(frameTargeter.target());
+
+ if (composite) {
+ mFlinger->composite(frameTargeter);
+ }
}
- TimePoint commit(TimePoint frameTime, VsyncId vsyncId) {
- return commit(frameTime, vsyncId, frameTime + Period(10ms));
+ void commit(TimePoint frameTime, VsyncId vsyncId, bool composite = false) {
+ return commit(frameTime, vsyncId, frameTime + Period(10ms), composite);
}
- TimePoint commit() {
+ void commit(bool composite = false) {
const TimePoint frameTime = scheduler::SchedulerClock::now();
- return commit(frameTime, kVsyncId);
+ commit(frameTime, kVsyncId, composite);
}
void commitAndComposite(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) {
- mFlinger->composite(commit(frameTime, vsyncId, expectedVsyncTime), vsyncId);
+ constexpr bool kComposite = true;
+ commit(frameTime, vsyncId, expectedVsyncTime, kComposite);
}
- void commitAndComposite() { mFlinger->composite(commit(), kVsyncId); }
+ void commitAndComposite() {
+ constexpr bool kComposite = true;
+ commit(kComposite);
+ }
auto createDisplay(const String8& displayName, bool secure, float requestedRefreshRate = 0.0f) {
return mFlinger->createDisplay(displayName, secure, requestedRefreshRate);
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 92411a7..809966f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -37,7 +37,7 @@
static constexpr size_t SMALL_BUFFER_SIZE = 1024;
TransactionTracing mTracing;
- void flush(int64_t vsyncId) { mTracing.flush(vsyncId); }
+ void flush() { mTracing.flush(); }
proto::TransactionTraceFile writeToProto() { return mTracing.writeToProto(); }
proto::TransactionTraceEntry bufferFront() {
@@ -57,7 +57,7 @@
std::vector<TransactionState> transactions;
update.transactions.emplace_back(transaction);
mTracing.addCommittedTransactions(vsyncId, 0, update, {}, false);
- flush(vsyncId);
+ flush();
}
void verifyEntry(const proto::TransactionTraceEntry& actualProto,
@@ -116,7 +116,7 @@
secondUpdate.transactions =
std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
mTracing.addCommittedTransactions(secondTransactionSetVsyncId, 0, secondUpdate, {}, false);
- flush(secondTransactionSetVsyncId);
+ flush();
proto::TransactionTraceFile proto = writeToProto();
ASSERT_EQ(proto.entry().size(), 2);
@@ -158,7 +158,7 @@
VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
mTracing.addCommittedTransactions(VSYNC_ID_FIRST_LAYER_CHANGE, 0, update, {}, false);
- flush(VSYNC_ID_FIRST_LAYER_CHANGE);
+ flush();
}
// add transactions that modify the layer state further so we can test that layer state
@@ -178,7 +178,7 @@
update.transactions.emplace_back(transaction);
VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
mTracing.addCommittedTransactions(VSYNC_ID_SECOND_LAYER_CHANGE, 0, update, {}, false);
- flush(VSYNC_ID_SECOND_LAYER_CHANGE);
+ flush();
}
// remove child layer
@@ -290,7 +290,7 @@
update.transactions.emplace_back(transaction);
mTracing.addCommittedTransactions(mVsyncId, 0, update, {}, false);
- flush(mVsyncId);
+ flush();
}
}
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index 3caa2b9..d635508 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -32,6 +32,7 @@
MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
(override));
MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
+ MOCK_METHOD(void, notifyCpuLoadUp, (), (override));
MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index af87306..b73d2cb 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1112,12 +1112,17 @@
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
imageFormatInfo.format =
pSurfaceFormats[i].surfaceFormat.format;
+ imageFormatInfo.type = VK_IMAGE_TYPE_2D;
+ imageFormatInfo.usage =
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
imageFormatInfo.pNext = nullptr;
VkImageCompressionControlEXT compressionControl = {};
compressionControl.sType =
VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT;
compressionControl.pNext = imageFormatInfo.pNext;
+ compressionControl.flags =
+ VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT;
imageFormatInfo.pNext = &compressionControl;